moving code around for a better file structure

This commit is contained in:
Chris Cochrun 2022-12-09 10:55:50 -06:00
parent 8f3e898385
commit 7eba697dc2
27 changed files with 20 additions and 24 deletions

187
src/cpp/filemanager.cpp Normal file
View file

@ -0,0 +1,187 @@
#include "filemanager.h"
#include <ktar.h>
#include <KCompressionDevice>
#include <KArchiveDirectory>
#include <KArchiveFile>
#include <KArchiveEntry>
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QFile>
#include <QTemporaryFile>
#include <QDir>
File::File(QObject *parent)
: QObject{parent}
{
qDebug() << "Initializing empty file";
}
File::File(const QString &name, const QString &filePath, QObject *parent)
: QObject(parent),m_name(name),m_filePath(filePath)
{
qDebug() << "Initializing file with defaults";
}
QString File::name() const {
return m_name;
}
QString File::filePath() const {
return m_filePath;
}
void File::setName(QString name)
{
if (m_name == name)
return;
qDebug() << "####changing name to: " << name;
m_name = name;
emit nameChanged(m_name);
}
void File::setFilePath(QString filePath)
{
if (m_filePath == filePath)
return;
qDebug() << "####changing filePath to: " << filePath;
m_filePath = filePath;
emit filePathChanged(m_filePath);
}
bool File::save(QUrl file, QVariantList serviceList) {
qDebug() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
qDebug() << "Saving...";
qDebug() << "File path is: " << file.toString();
qDebug() << "serviceList is: " << serviceList;
qDebug() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
//first we'll get a json representation of our serviceList
//save that to a temp file in case we need it but also convert
//it to a byte array just before putting it into the archive
QJsonArray jsonData;
// save all the audio and files in jsonData as just the base name
// so that they are properly mapped in the resulting archive
for (int i = 0; i < serviceList.length(); i++) {
// qDebug() << serviceList[i];
QMap item = serviceList[i].toMap();
qDebug() << "AUDIO IS: " << item.value("audio").toString();
QFileInfo audioFile = item.value("audio").toString();
qDebug() << audioFile.fileName();
item["flatAudio"] = audioFile.fileName();
qDebug() << "AUDIO IS NOW: " << item.value("audio").toString();
QFileInfo backgroundFile = item.value("background").toString();
item["flatBackground"] = backgroundFile.fileName();
qDebug() << "BACKGRUOND IS: " << item.value("background").toString();
// qDebug() << serviceList[i].value();
QJsonObject obj = QJsonObject::fromVariantMap(item);
qDebug() << obj;
jsonData.insert(i, obj);
}
qDebug() << jsonData;
QJsonDocument jsonText(jsonData);
QTemporaryFile jsonFile;
if (!jsonFile.exists())
qDebug() << "NOT EXISTS!";
if (!jsonFile.open())
return false;
//finalize the temp json file, in case something goes wrong in the
//archive, we'll have this to jump back to
jsonFile.write(jsonText.toJson());
qDebug() << jsonFile.fileName();
jsonFile.close();
//now we create our archive file and set it's parameters
QString filename = file.toString().right(file.toString().size() - 7);
qDebug() << filename;
// KCompressionDevice dev(filename, KCompressionDevice::Zstd);
// if (!dev.open(QIODevice::WriteOnly)) {
// qDebug() << dev.isOpen();
// return false;
// }
KTar tar(filename, "application/zstd");
if (tar.open(QIODevice::WriteOnly)) {
qDebug() << tar.isOpen();
//write our json data to the archive
tar.writeFile("servicelist.json",
jsonText.toJson());
//let's add the backgrounds and audios to the archive
for (int i = 0; i < serviceList.size(); i++) {
QMap item = serviceList[i].toMap();
QString background = item.value("background").toString();
QString backgroundFile = background.right(background.size() - 5);
qDebug() << backgroundFile;
QString audio = item.value("audio").toString();
QString audioFile = audio.right(audio.size() - 5);
qDebug() << audioFile;
//here we need to cut off all the directories before
//adding into the archive
tar.addLocalFile(backgroundFile,
backgroundFile.right(backgroundFile.size() -
backgroundFile.lastIndexOf("/") - 1));
tar.addLocalFile(audioFile,
audioFile.right(audioFile.size() -
audioFile.lastIndexOf("/") - 1));
}
//close the archive so that everything is done
tar.close();
// dev.close();
return true;
}
return false;
}
QVariantList File::load(QUrl file) {
qDebug() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
qDebug() << "Loading...";
qDebug() << "File path is: " << file.toString();
qDebug() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
QString fileUrl = file.toString().right(file.toString().size() - 7);
KTar tar(fileUrl);
if (tar.open(QIODevice::ReadOnly)){
qDebug() << tar.isOpen();
const KArchiveDirectory *dir = tar.directory();
const KArchiveEntry *e = dir->entry("servicelist.json");
if (!e) {
qDebug() << "File not found!";
}
const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
QByteArray arr(f->data());
QJsonDocument jsonText = QJsonDocument::fromJson(arr);
qDebug() << jsonText; // the file contents
QJsonArray array = jsonText.array();
QVariantList serviceList = array.toVariantList();
qDebug() << serviceList;
return serviceList;
}
QVariantList blankList;
return blankList;
}

40
src/cpp/filemanager.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef FILEMANAGER_H
#define FILEMANAGER_H
#include <qobjectdefs.h>
#include <qqml.h>
#include <QObject>
#include <qobject.h>
class File : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString filePath READ filePath WRITE setFilePath NOTIFY filePathChanged)
// QML_ELEMENT
public:
explicit File(QObject *parent = nullptr);
File(const QString &name, const QString &filePath,
QObject * parent = nullptr);
QString name() const;
QString filePath() const;
Q_INVOKABLE void setName(QString name);
Q_INVOKABLE void setFilePath(QString filePath);
Q_INVOKABLE bool save(QUrl file, QVariantList serviceList);
Q_INVOKABLE QVariantList load(QUrl file);
signals:
Q_INVOKABLE void nameChanged(QString name);
Q_INVOKABLE void filePathChanged(QString filePath);
private:
QString m_name;
QString m_filePath;
};
#endif //FILEMANAGER_H

204
src/cpp/imagesqlmodel.cpp Normal file
View file

@ -0,0 +1,204 @@
#include "imagesqlmodel.h"
#include <QDateTime>
#include <QDebug>
#include <QSqlError>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QSql>
#include <QSqlDatabase>
#include <QFileInfo>
#include <qabstractitemmodel.h>
#include <qdebug.h>
#include <qnamespace.h>
#include <qobject.h>
#include <qobjectdefs.h>
#include <qsqlrecord.h>
#include <qurl.h>
#include <qvariant.h>
static const char *imagesTableName = "images";
static void createImageTable()
{
if(QSqlDatabase::database().tables().contains(imagesTableName)) {
return;
}
QSqlQuery query;
if (!query.exec("CREATE TABLE IF NOT EXISTS 'images' ("
" 'id' INTEGER NOT NULL,"
" 'title' TEXT NOT NULL,"
" 'filePath' TEXT NOT NULL,"
" PRIMARY KEY(id))")) {
qFatal("Failed to query database: %s",
qPrintable(query.lastError().text()));
}
qDebug() << query.lastQuery();
qDebug() << "inserting into images";
query.exec("INSERT INTO images (title, filePath) VALUES ('Dec 180', 'file:///home/chris/nextcloud/tfc/openlp/180-dec.png')");
qDebug() << query.lastQuery();
query.exec("INSERT INTO images (title, filePath) VALUES ('No TFC', "
"'file:///home/chris/nextcloud/tfc/openlp/No TFC.png')");
query.exec("select * from images");
qDebug() << query.lastQuery();
}
ImageSqlModel::ImageSqlModel(QObject *parent) : QSqlTableModel(parent) {
qDebug() << "creating image table";
createImageTable();
setTable(imagesTableName);
setEditStrategy(QSqlTableModel::OnManualSubmit);
// make sure to call select else the model won't fill
select();
}
QVariant ImageSqlModel::data(const QModelIndex &index, int role) const {
if (role < Qt::UserRole) {
return QSqlTableModel::data(index, role);
}
// qDebug() << role;
const QSqlRecord sqlRecord = record(index.row());
return sqlRecord.value(role - Qt::UserRole);
}
QHash<int, QByteArray> ImageSqlModel::roleNames() const
{
QHash<int, QByteArray> names;
names[Qt::UserRole] = "id";
names[Qt::UserRole + 1] = "title";
names[Qt::UserRole + 2] = "filePath";
return names;
}
void ImageSqlModel::newImage(const QUrl &filePath) {
qDebug() << "adding new image";
int rows = rowCount();
qDebug() << rows;
QSqlRecord recordData = record();
QFileInfo fileInfo = filePath.toString();
QString title = fileInfo.baseName();
recordData.setValue("title", title);
recordData.setValue("filePath", filePath);
if (insertRecord(rows, recordData)) {
submitAll();
} else {
qDebug() << lastError();
};
}
void ImageSqlModel::deleteImage(const int &row) {
QSqlRecord recordData = record(row);
if (recordData.isEmpty())
return;
removeRow(row);
submitAll();
}
int ImageSqlModel::id() const {
return m_id;
}
QString ImageSqlModel::title() const {
return m_title;
}
void ImageSqlModel::setTitle(const QString &title) {
if (title == m_title)
return;
m_title = title;
select();
emit titleChanged();
}
// This function is for updating the title from outside the delegate
void ImageSqlModel::updateTitle(const int &row, const QString &title) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from images");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
// qDebug() << rowdata.value(0);
rowdata.setValue("title", title);
bool suc = setRecord(id, rowdata);
qDebug() << "#############";
qDebug() << rowdata.value("title");
qDebug() << "was it successful? " << suc;
qDebug() << "#############";
bool suca = submitAll();
// qDebug() << suca;
emit titleChanged();
}
QUrl ImageSqlModel::filePath() const {
return m_filePath;
}
void ImageSqlModel::setFilePath(const QUrl &filePath) {
if (filePath == m_filePath)
return;
m_filePath = filePath;
select();
emit filePathChanged();
}
// This function is for updating the filepath from outside the delegate
void ImageSqlModel::updateFilePath(const int &row, const QUrl &filePath) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from images");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("filePath", filePath);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit filePathChanged();
}
QVariantMap ImageSqlModel::getImage(const int &row) {
// qDebug() << "Row we are getting is " << row;
// QUrl image;
// QSqlRecord rec = record(row);
// qDebug() << rec.value("filePath").toUrl();
// // image.append(rec.value("title"));
// // image.append(rec.value("filePath"));
// image = rec.value("filePath").toUrl();
// return image;
QVariantMap data;
const QModelIndex idx = this->index(row,0);
// qDebug() << idx;
if( !idx.isValid() )
return data;
const QHash<int,QByteArray> rn = roleNames();
// qDebug() << rn;
QHashIterator<int,QByteArray> it(rn);
while (it.hasNext()) {
it.next();
qDebug() << it.key() << ":" << it.value();
data[it.value()] = idx.data(it.key());
}
return data;
}

49
src/cpp/imagesqlmodel.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef IMAGESQLMODEL_H
#define IMAGESQLMODEL_H
#include <QSqlTableModel>
#include <qobject.h>
#include <qobjectdefs.h>
#include <qqml.h>
#include <qurl.h>
#include <qvariant.h>
class ImageSqlModel : public QSqlTableModel
{
Q_OBJECT
Q_PROPERTY(int id READ id)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QUrl filePath READ filePath WRITE setFilePath NOTIFY filePathChanged)
QML_ELEMENT
public:
ImageSqlModel(QObject *parent = 0);
int id() const;
QString title() const;
QUrl filePath() const;
void setTitle(const QString &title);
void setFilePath(const QUrl &filePath);
Q_INVOKABLE void updateTitle(const int &row, const QString &title);
Q_INVOKABLE void updateFilePath(const int &row, const QUrl &filePath);
Q_INVOKABLE void newImage(const QUrl &filePath);
Q_INVOKABLE void deleteImage(const int &row);
Q_INVOKABLE QVariantMap getImage(const int &row);
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
signals:
void titleChanged();
void filePathChanged();
private:
int m_id;
QString m_title;
QUrl m_filePath;
};
#endif //IMAGESQLMODEL_H

159
src/cpp/main.cpp Normal file
View file

@ -0,0 +1,159 @@
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QUrl>
#include <QSql>
#include <QDebug>
#include <KLocalizedContext>
#include <KLocalizedString>
#include <KAboutData>
#include <iostream>
#include <QQmlEngine>
#include <QtSql>
#include <QSqlDatabase>
#include <QSqlTableModel>
#include <QObject>
#include <QtGlobal>
#include <QOpenGLContext>
#include <QGuiApplication>
#include <QQuickStyle>
#include <QtGui/QOpenGLFramebufferObject>
#include <QtQuick/QQuickWindow>
#include <QtQuick/QQuickView>
#include <qapplication.h>
#include <qcoreapplication.h>
#include <qdir.h>
#include <qglobal.h>
#include <qguiapplication.h>
#include <qqml.h>
#include <qquickstyle.h>
#include <qsqldatabase.h>
#include <qsqlquery.h>
#include <qstringliteral.h>
#include "mpv/mpvobject.h"
#include "serviceitemmodel.h"
#include "songsqlmodel.h"
#include "videosqlmodel.h"
#include "imagesqlmodel.h"
#include "presentationsqlmodel.h"
#include "filemanager.h"
#include "slide.h"
// RUST
#include "cxx-qt-gen/my_object.cxxqt.h"
static void connectToDatabase() {
// let's setup our sql database
QSqlDatabase db = QSqlDatabase::database();
if (!db.isValid()){
db = QSqlDatabase::addDatabase("QSQLITE");
if (!db.isValid())
qFatal("Cannot add database: %s", qPrintable(db.lastError().text()));
}
const QDir writeDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
qDebug() << "dir location " << writeDir.absolutePath();
if (!writeDir.mkpath(".")) {
qFatal("Failed to create writable location at %s", qPrintable(writeDir.absolutePath()));
}
const QString dbName = writeDir.absolutePath() + "/library-db.sqlite3";
db.setHostName("localhost");
db.setDatabaseName(dbName);
db.setUserName("presenter");
// TODO change password system before launch
db.setPassword("i393jkf782djyr98302j");
if (!db.open()) {
qFatal("Cannot open database: %s", qPrintable(db.lastError().text()));
QFile::remove(dbName);
}
}
int main(int argc, char *argv[])
{
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("system-config-display")));
QApplication app(argc, argv);
KLocalizedString::setApplicationDomain("librepresenter");
KAboutData aboutData("librepresenter", i18n("Libre Presenter"), "0.1",
i18n("A church presentation app built with KDE tech."),
KAboutLicense::GPL_V3,
i18n("Copyright 2017 Bar Foundation"), QString(),
"https://www.foo-the-app.net");
// overwrite default-generated values of organizationDomain & desktopFileName
aboutData.setOrganizationDomain("tfcconnection.org");
aboutData.setDesktopFileName("org.tfcconnection.librepresenter");
// set the application metadata
KAboutData::setApplicationData(aboutData);
QCoreApplication::setOrganizationName(QStringLiteral("librepresenter"));
QCoreApplication::setOrganizationDomain(QStringLiteral("tfcconnection.org"));
QCoreApplication::setApplicationName(QStringLiteral("Libre Presenter"));
qSetMessagePattern("[%{type} %{time h:m:s ap}: %{function} in %{file}]: %{message}\n");
#ifdef Q_OS_WINDOWS
QIcon::setFallbackThemeName("breeze");
QQuickStyle::setStyle(QStringLiteral("org.kde.breeze"));
// QApplication::setStyle(QStringLiteral("breeze"));
#else
QIcon::setFallbackThemeName("breeze");
QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
QQuickStyle::setFallbackStyle(QStringLiteral("Default"));
#endif
qDebug() << QQuickStyle::availableStyles();
qDebug() << QIcon::themeName();
// integrate with commandline argument handling
QCommandLineParser parser;
aboutData.setupCommandLine(&parser);
// setup of app specific commandline args
//Need to instantiate our slide
QScopedPointer<Slide> slide(new Slide);
QScopedPointer<File> filemanager(new File);
// apparently mpv needs this class set
// let's register mpv as well
std::setlocale(LC_NUMERIC, "C");
qmlRegisterType<MpvObject>("mpv", 1, 0, "MpvObject");
//register our models
qmlRegisterType<SongSqlModel>("org.presenter", 1, 0, "SongSqlModel");
qmlRegisterType<VideoSqlModel>("org.presenter", 1, 0, "VideoSqlModel");
qmlRegisterType<ImageSqlModel>("org.presenter", 1, 0, "ImageSqlModel");
qmlRegisterType<PresentationSqlModel>("org.presenter", 1, 0, "PresentationSqlModel");
qmlRegisterType<ServiceItemModel>("org.presenter", 1, 0, "ServiceItemModel");
qmlRegisterType<MyObject>("org.presenter", 1, 0, "MyObject");
qmlRegisterSingletonInstance("org.presenter", 1, 0, "SlideObject", slide.get());
qmlRegisterSingletonInstance("org.presenter", 1, 0, "FileManager", filemanager.get());
connectToDatabase();
QQmlApplicationEngine engine;
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
engine.load(QUrl(QStringLiteral("qrc:qml/main.qml")));
// QQuickView *view = new QQuickView;
// view->setSource(QUrl(QStringLiteral("qrc:qml/main.qml")));
// view->show();
#ifdef STATIC_KIRIGAMI
KirigamiPlugin::getInstance().registerTypes();
#endif
if (engine.rootObjects().isEmpty()) {
return -1;
}
return app.exec();
}

154
src/cpp/mpv/mpvhelpers.h Normal file
View file

@ -0,0 +1,154 @@
#pragma once
// MpvObject definition
#define READONLY_PROP_BOOL(p, varName) \
public: \
Q_PROPERTY(bool varName READ varName NOTIFY varName##Changed) \
public Q_SLOTS: \
bool varName() const { return getProperty(p).toBool(); } \
Q_SIGNALS: \
void varName##Changed(bool value);
#define WRITABLE_PROP_BOOL(p, varName) \
public: \
Q_PROPERTY(bool varName READ varName WRITE set_##varName NOTIFY varName##Changed) \
public Q_SLOTS: \
bool varName() const { return getProperty(p).toBool(); } \
void set_##varName(bool value) { setProperty(p, value); } \
Q_SIGNALS: \
void varName##Changed(bool value);
#define READONLY_PROP_INT(p, varName) \
public: \
Q_PROPERTY(int varName READ varName NOTIFY varName##Changed) \
public Q_SLOTS: \
int varName() { return getProperty(p).toInt(); } \
Q_SIGNALS: \
void varName##Changed(int value);
#define WRITABLE_PROP_INT(p, varName) \
public: \
Q_PROPERTY(int varName READ varName WRITE set_##varName NOTIFY varName##Changed) \
public Q_SLOTS: \
int varName() { return getProperty(p).toInt(); } \
void set_##varName(int value) { setProperty(p, value); } \
Q_SIGNALS: \
void varName##Changed(int value);
#define READONLY_PROP_DOUBLE(p, varName) \
public: \
Q_PROPERTY(double varName READ varName NOTIFY varName##Changed) \
public Q_SLOTS: \
double varName() { return getProperty(p).toDouble(); } \
Q_SIGNALS: \
void varName##Changed(double value);
#define WRITABLE_PROP_DOUBLE(p, varName) \
public: \
Q_PROPERTY(double varName READ varName WRITE set_##varName NOTIFY varName##Changed) \
public Q_SLOTS: \
double varName() { return getProperty(p).toDouble(); } \
void set_##varName(double value) { setProperty(p, value); } \
Q_SIGNALS: \
void varName##Changed(double value);
#define READONLY_PROP_STRING(p, varName) \
public: \
Q_PROPERTY(QString varName READ varName NOTIFY varName##Changed) \
public Q_SLOTS: \
QString varName() { return getProperty(p).toString(); } \
Q_SIGNALS: \
void varName##Changed(QString value);
#define WRITABLE_PROP_STRING(p, varName) \
public: \
Q_PROPERTY(QString varName READ varName WRITE set_##varName NOTIFY varName##Changed) \
public Q_SLOTS: \
QString varName() { return getProperty(p).toString(); } \
void set_##varName(QString value) { setProperty(p, value); } \
Q_SIGNALS: \
void varName##Changed(QString value);
#define READONLY_PROP_ARRAY(p, varName) \
public: \
Q_PROPERTY(QVariantList varName READ varName NOTIFY varName##Changed) \
public Q_SLOTS: \
QVariantList varName() { return getProperty(p).toList(); } \
Q_SIGNALS: \
void varName##Changed(QVariantList value);
#define WRITABLE_PROP_ARRAY(p, varName) \
public: \
Q_PROPERTY(QVariantList varName READ varName WRITE set_##varName NOTIFY varName##Changed) \
public Q_SLOTS: \
QVariantList varName() { return getProperty(p).toList(); } \
void set_##varName(QVariantList value) { setProperty(p, value); } \
Q_SIGNALS: \
void varName##Changed(QVariantList value);
#define READONLY_PROP_MAP(p, varName) \
public: \
Q_PROPERTY(QVariantMap varName READ varName NOTIFY varName##Changed) \
public Q_SLOTS: \
QVariantMap varName() { return getProperty(p).toMap(); } \
Q_SIGNALS: \
void varName##Changed(QVariantMap value);
#define WRITABLE_PROP_MAP(p, varName) \
public: \
Q_PROPERTY(QVariantMap varName READ varName WRITE set_##varName NOTIFY varName##Changed) \
public Q_SLOTS: \
QVariantMap varName() { return getProperty(p).toMap(); } \
void set_##varName(QVariantMap value) { setProperty(p, value); } \
Q_SIGNALS: \
void varName##Changed(QVariantMap value);
// MpvObject() constructor
#define WATCH_PROP_BOOL(p) \
mpv_observe_property(mpv, 0, p, MPV_FORMAT_FLAG);
#define WATCH_PROP_DOUBLE(p) \
mpv_observe_property(mpv, 0, p, MPV_FORMAT_DOUBLE);
#define WATCH_PROP_INT(p) \
mpv_observe_property(mpv, 0, p, MPV_FORMAT_INT64);
#define WATCH_PROP_STRING(p) \
mpv_observe_property(mpv, 0, p, MPV_FORMAT_STRING);
#define WATCH_PROP_ARRAY(p) \
mpv_observe_property(mpv, 0, p, MPV_FORMAT_NODE_ARRAY);
#define WATCH_PROP_MAP(p) \
mpv_observe_property(mpv, 0, p, MPV_FORMAT_NODE_MAP);
// MpvObject::handle_mpv_event()
#define HANDLE_PROP_NONE(p, varName) \
(strcmp(prop->name, p) == 0) { \
int64_t value = 0; \
Q_EMIT varName##Changed(value); \
}
#define HANDLE_PROP_BOOL(p, varName) \
(strcmp(prop->name, p) == 0) { \
bool value = *(bool *)prop->data; \
Q_EMIT varName##Changed(value); \
}
#define HANDLE_PROP_INT(p, varName) \
(strcmp(prop->name, p) == 0) { \
int64_t value = *(int64_t *)prop->data; \
Q_EMIT varName##Changed(value); \
}
#define HANDLE_PROP_DOUBLE(p, varName) \
(strcmp(prop->name, p) == 0) { \
double value = *(double *)prop->data; \
Q_EMIT varName##Changed(value); \
}
#define HANDLE_PROP_STRING(p, varName) \
(strcmp(prop->name, p) == 0) { \
char* charValue = *(char**)prop->data; \
QString value = QString::fromUtf8(charValue); \
Q_EMIT varName##Changed(value); \
}
#define HANDLE_PROP_ARRAY(p, varName) \
(strcmp(prop->name, p) == 0) { \
QVariantList value = getProperty(p).toList(); \
Q_EMIT varName##Changed(value); \
}
#define HANDLE_PROP_MAP(p, varName) \
(strcmp(prop->name, p) == 0) { \
QVariantMap value = getProperty(p).toMap(); \
Q_EMIT varName##Changed(value); \
}

570
src/cpp/mpv/mpvobject.cpp Normal file
View file

@ -0,0 +1,570 @@
#include "mpvobject.h"
// std
#include <mpv/render.h>
#include <qdir.h>
#include <qvariant.h>
#include <stdexcept>
#include <clocale>
// Qt
#include <QObject>
#include <QtGlobal>
#include <QOpenGLContext>
#include <QGuiApplication>
#include <QtQuick/QQuickWindow>
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickFramebufferObject>
#include <QtGui/QOpenGLFramebufferObject>
#include <QtX11Extras/QX11Info>
#include <QDebug>
#include <QStandardPaths>
// libmpv
#include <mpv/client.h>
#include <mpv/render_gl.h>
// own
#include "qthelper.hpp"
const QDir writeDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
//--- MpvRenderer
void* MpvRenderer::get_proc_address(void *ctx, const char *name) {
(void)ctx;
QOpenGLContext *glctx = QOpenGLContext::currentContext();
if (!glctx)
return nullptr;
return reinterpret_cast<void *>(glctx->getProcAddress(QByteArray(name)));
}
MpvRenderer::MpvRenderer(const MpvObject *obj)
: obj(obj)
, mpv_gl(nullptr)
{
// https://github.com/mpv-player/mpv/blob/master/libmpv/render_gl.h#L106
#if MPV_CLIENT_API_VERSION >= MPV_MAKE_VERSION(2, 0)
mpv_opengl_init_params gl_init_params{
get_proc_address,
nullptr // get_proc_address_ctx
};
#else
mpv_opengl_init_params gl_init_params{
get_proc_address,
nullptr, // get_proc_address_ctx
nullptr // extra_exts (deprecated)
};
#endif
mpv_render_param display{
MPV_RENDER_PARAM_INVALID,
nullptr
};
if (QX11Info::isPlatformX11()) {
display.type = MPV_RENDER_PARAM_X11_DISPLAY;
display.data = QX11Info::display();
}
mpv_render_param params[]{
{ MPV_RENDER_PARAM_API_TYPE, const_cast<char *>(MPV_RENDER_API_TYPE_OPENGL) },
{ MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params },
display,
{ MPV_RENDER_PARAM_INVALID, nullptr }
};
if (mpv_render_context_create(&mpv_gl, obj->mpv, params) < 0)
throw std::runtime_error("failed to initialize mpv GL context");
mpv_render_context_set_update_callback(mpv_gl, MpvObject::on_update, (void *)obj);
}
MpvRenderer::~MpvRenderer() {
if (mpv_gl)
mpv_render_context_free(mpv_gl);
// mpv_destroy(obj->mpv);
}
void MpvRenderer::render() {
QOpenGLFramebufferObject *fbo = framebufferObject();
// fbo->bind();
obj->window()->resetOpenGLState();
// https://github.com/mpv-player/mpv/blob/master/libmpv/render_gl.h#L133
mpv_opengl_fbo mpfbo{
.fbo = static_cast<int>(fbo->handle()),
.w = fbo->width(),
.h = fbo->height(),
.internal_format = 0 // 0=unknown
};
mpv_render_param params[] = {
{ MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo },
{ MPV_RENDER_PARAM_INVALID, nullptr }
};
mpv_render_context_render(mpv_gl, params);
obj->window()->resetOpenGLState();
// fbo->release();
}
//--- MpvObject
static void wakeup(void *ctx)
{
QMetaObject::invokeMethod((MpvObject*)ctx, "onMpvEvents", Qt::QueuedConnection);
}
MpvObject::MpvObject(QQuickItem *parent)
: QQuickFramebufferObject(parent)
, m_enableAudio(true)
, m_useHwdec(false)
, m_duration(0)
, m_position(0)
, m_isPlaying(false)
{
mpv = mpv_create();
if (!mpv)
throw std::runtime_error("could not create mpv context");
// these are the terminal messages
// mpv_set_option_string(mpv, "terminal", "yes");
// mpv_set_option_string(mpv, "msg-level", "all=warn,ao/alsa=error");
// mpv_set_option_string(mpv, "msg-level", "all=debug");
//--- Hardware Decoding
mpv::qt::set_option_variant(mpv, "hwdec-codecs", "all");
if (mpv_initialize(mpv) < 0)
throw std::runtime_error("could not initialize mpv context");
mpv::qt::set_option_variant(mpv, "audio-client-name", "mpvz");
mpv_request_log_messages(mpv, "terminal-default");
//--- 60fps Interpolation
mpv::qt::set_option_variant(mpv, "interpolation", "yes");
mpv::qt::set_option_variant(mpv, "video-sync", "display-resample");
// mpv::qt::set_option_variant(mpv, "vf", "lavfi=\"fps=fps=60:round=down\"");
// mpv::qt::set_option_variant(mpv, "override-display-fps", "60");
//--- ytdl 1080p max
mpv::qt::set_option_variant(mpv, "ytdl-format", "ytdl-format=bestvideo[width<=?720]+bestaudio/best");
mpv::qt::set_option_variant(mpv, "quiet", "yes");
// Setup the callback that will make QtQuick update and redraw if there
// is a new video frame. Use a queued connection: this makes sure the
// doUpdate() function is run on the GUI thread.
// * MpvRender binds mpv_gl update function to MpvObject::on_update
// * MpvObject::on_update will emit MpvObject::mpvUpdated
connect(this, &MpvObject::mpvUpdated,
this, &MpvObject::doUpdate,
Qt::QueuedConnection);
WATCH_PROP_BOOL("idle-active")
WATCH_PROP_BOOL("mute")
WATCH_PROP_BOOL("pause")
WATCH_PROP_BOOL("paused-for-cache")
WATCH_PROP_BOOL("seekable")
WATCH_PROP_INT("chapter")
WATCH_PROP_INT("chapter-list/count")
WATCH_PROP_INT("decoder-frame-drop-count")
WATCH_PROP_INT("dheight")
WATCH_PROP_INT("dwidth")
WATCH_PROP_INT("estimated-frame-count")
WATCH_PROP_INT("estimated-frame-number")
WATCH_PROP_INT("frame-drop-count")
WATCH_PROP_INT("playlist-pos")
WATCH_PROP_INT("playlist/count")
WATCH_PROP_INT("vo-delayed-frame-count")
WATCH_PROP_INT("volume")
WATCH_PROP_INT("vid")
WATCH_PROP_INT("aid")
WATCH_PROP_INT("sid")
WATCH_PROP_INT("audio-params/channel-count")
WATCH_PROP_INT("audio-params/samplerate")
WATCH_PROP_INT("track-list/count")
WATCH_PROP_INT("contrast")
WATCH_PROP_INT("brightness")
WATCH_PROP_INT("gamma")
WATCH_PROP_INT("saturation")
WATCH_PROP_INT("sub-margin-y")
WATCH_PROP_DOUBLE("audio-bitrate")
WATCH_PROP_DOUBLE("avsync")
WATCH_PROP_DOUBLE("container-fps")
WATCH_PROP_DOUBLE("demuxer-cache-duration")
WATCH_PROP_DOUBLE("display-fps")
WATCH_PROP_DOUBLE("duration")
WATCH_PROP_DOUBLE("estimated-display-fps")
WATCH_PROP_DOUBLE("estimated-vf-fps")
WATCH_PROP_DOUBLE("fps")
WATCH_PROP_DOUBLE("speed")
WATCH_PROP_DOUBLE("time-pos")
WATCH_PROP_DOUBLE("video-bitrate")
WATCH_PROP_DOUBLE("video-params/aspect")
WATCH_PROP_DOUBLE("video-out-params/aspect")
WATCH_PROP_DOUBLE("window-scale")
WATCH_PROP_DOUBLE("current-window-scale")
WATCH_PROP_STRING("audio-codec")
WATCH_PROP_STRING("audio-codec-name")
WATCH_PROP_STRING("audio-params/format")
WATCH_PROP_STRING("filename")
WATCH_PROP_STRING("file-format")
WATCH_PROP_STRING("file-size")
WATCH_PROP_STRING("audio-format")
WATCH_PROP_STRING("hwdec")
WATCH_PROP_STRING("hwdec-current")
WATCH_PROP_STRING("hwdec-interop")
WATCH_PROP_STRING("loop")
WATCH_PROP_STRING("media-title")
WATCH_PROP_STRING("path")
WATCH_PROP_STRING("video-codec")
WATCH_PROP_STRING("video-format")
WATCH_PROP_STRING("video-params/pixelformat")
WATCH_PROP_STRING("video-out-params/pixelformat")
WATCH_PROP_STRING("ytdl-format")
WATCH_PROP_MAP("demuxer-cache-state")
connect(this, &MpvObject::idleActiveChanged,
this, &MpvObject::updateState);
connect(this, &MpvObject::pausedChanged,
this, &MpvObject::updateState);
mpv_set_wakeup_callback(mpv, wakeup, this);
}
MpvObject::~MpvObject()
{
// quit();
}
QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const
{
window()->setPersistentOpenGLContext(true);
window()->setPersistentSceneGraph(true);
return new MpvRenderer(this);
}
void MpvObject::on_update(void *ctx)
{
MpvObject *self = (MpvObject *)ctx;
emit self->mpvUpdated();
}
// connected to mpvUpdated(); signal makes sure it runs on the GUI thread
void MpvObject::doUpdate()
{
update();
}
void MpvObject::command(const QVariant& params)
{
qDebug() << params;
mpv::qt::command(mpv, params);
}
void MpvObject::commandAsync(const QVariant& params)
{
// qDebug() << params;
mpv::qt::command_async(mpv, params);
}
void MpvObject::setProperty(const QString& name, const QVariant& value)
{
mpv::qt::set_property_variant(mpv, name, value);
}
QVariant MpvObject::getProperty(const QString &name) const
{
return mpv::qt::get_property_variant(mpv, name);
}
void MpvObject::setOption(const QString& name, const QVariant& value)
{
mpv::qt::set_option_variant(mpv, name, value);
}
void MpvObject::onMpvEvents()
{
// Process all events, until the event queue is empty.
while (mpv) {
mpv_event *event = mpv_wait_event(mpv, 0);
if (event->event_id == MPV_EVENT_NONE) {
break;
}
handle_mpv_event(event);
}
}
void MpvObject::logPropChange(mpv_event_property *prop)
{
switch (prop->format) {
case MPV_FORMAT_NONE:
qDebug() << objectName() << "none" << prop->name << 0;
break;
case MPV_FORMAT_STRING:
qDebug() << objectName() << "str " << prop->name << *(char**)prop->data;
break;
case MPV_FORMAT_FLAG:
qDebug() << objectName() << "bool" << prop->name << *(bool *)prop->data;
break;
case MPV_FORMAT_INT64:
qDebug() << objectName() << "int " << prop->name << *(int64_t *)prop->data;
break;
case MPV_FORMAT_DOUBLE:
qDebug() << objectName() << "doub" << prop->name << *(double *)prop->data;
break;
case MPV_FORMAT_NODE_ARRAY:
qDebug() << objectName() << "arr " << prop->name; // TODO
break;
case MPV_FORMAT_NODE_MAP:
qDebug() << objectName() << "map " << prop->name; // TODO
break;
default:
qDebug() << objectName() << "prop(format=" << prop->format << ")" << prop->name;
break;
}
}
void MpvObject::handle_mpv_event(mpv_event *event)
{
// See: https://github.com/mpv-player/mpv/blob/master/libmpv/client.h
// See: https://github.com/mpv-player/mpv/blob/master/player/lua.c#L471
switch (event->event_id) {
case MPV_EVENT_SHUTDOWN: {
mpv_destroy(mpv);
break;
}
case MPV_EVENT_LOG_MESSAGE: {
mpv_event_log_message *logData = (mpv_event_log_message *)event->data;
Q_EMIT logMessage(
QString(logData->prefix),
QString(logData->level),
QString(logData->text)
);
break;
}
case MPV_EVENT_START_FILE: {
Q_EMIT fileStarted();
break;
}
case MPV_EVENT_END_FILE: {
mpv_event_end_file *eef = (mpv_event_end_file *)event->data;
const char *reason;
switch (eef->reason) {
case MPV_END_FILE_REASON_EOF: reason = "eof"; break;
case MPV_END_FILE_REASON_STOP: reason = "stop"; break;
case MPV_END_FILE_REASON_QUIT: reason = "quit"; break;
case MPV_END_FILE_REASON_ERROR: reason = "error"; break;
case MPV_END_FILE_REASON_REDIRECT: reason = "redirect"; break;
default:
reason = "unknown";
}
Q_EMIT fileEnded(QString(reason));
break;
}
case MPV_EVENT_FILE_LOADED: {
Q_EMIT fileLoaded();
break;
}
case MPV_EVENT_PROPERTY_CHANGE: {
mpv_event_property *prop = (mpv_event_property *)event->data;
// logPropChange(prop);
if (prop->format == MPV_FORMAT_NONE) {
if HANDLE_PROP_NONE("vid", vid)
else if HANDLE_PROP_NONE("aid", aid)
else if HANDLE_PROP_NONE("sid", sid)
else if HANDLE_PROP_NONE("track-list/count", trackListCount)
} else if (prop->format == MPV_FORMAT_DOUBLE) {
if (strcmp(prop->name, "time-pos") == 0) {
double time = *(double *)prop->data;
m_position = time;
Q_EMIT positionChanged(time);
} else if (strcmp(prop->name, "duration") == 0) {
double time = *(double *)prop->data;
m_duration = time;
Q_EMIT durationChanged(time);
}
else if HANDLE_PROP_DOUBLE("audio-bitrate", audioBitrate)
else if HANDLE_PROP_DOUBLE("avsync", avsync)
else if HANDLE_PROP_DOUBLE("container-fps", containerFps)
else if HANDLE_PROP_DOUBLE("demuxer-cache-duration", demuxerCacheDuration)
else if HANDLE_PROP_DOUBLE("display-fps", displayFps)
else if HANDLE_PROP_DOUBLE("estimated-display-fps", estimatedDisplayFps)
else if HANDLE_PROP_DOUBLE("estimated-vf-fps", estimatedVfFps)
else if HANDLE_PROP_DOUBLE("fps", fps)
else if HANDLE_PROP_DOUBLE("speed", speed)
else if HANDLE_PROP_DOUBLE("video-bitrate", videoBitrate)
else if HANDLE_PROP_DOUBLE("video-params/aspect", videoParamsAspect)
else if HANDLE_PROP_DOUBLE("video-out-params/aspect", videoOutParamsAspect)
else if HANDLE_PROP_DOUBLE("window-scale", windowScale)
else if HANDLE_PROP_DOUBLE("current-window-scale", currentWindowScale)
} else if (prop->format == MPV_FORMAT_FLAG) {
if HANDLE_PROP_BOOL("idle-active", idleActive)
else if HANDLE_PROP_BOOL("mute", muted)
else if HANDLE_PROP_BOOL("pause", paused)
else if HANDLE_PROP_BOOL("paused-for-cache", pausedForCache)
else if HANDLE_PROP_BOOL("seekable", seekable)
} else if (prop->format == MPV_FORMAT_STRING) {
if HANDLE_PROP_STRING("audio-codec", audioCodec)
else if HANDLE_PROP_STRING("audio-codec-name", audioCodecName)
else if HANDLE_PROP_STRING("audio-params/format", audioParamsFormat)
else if HANDLE_PROP_STRING("filename", filename)
else if HANDLE_PROP_STRING("file-format", fileFormat)
else if HANDLE_PROP_STRING("file-size", fileSize)
else if HANDLE_PROP_STRING("audio-format", audioFormat)
else if HANDLE_PROP_STRING("hwdec", hwdec)
else if HANDLE_PROP_STRING("hwdec-current", hwdecCurrent)
else if HANDLE_PROP_STRING("hwdec-interop", hwdecInterop)
else if HANDLE_PROP_STRING("loop", loop)
else if HANDLE_PROP_STRING("media-title", mediaTitle)
else if HANDLE_PROP_STRING("path", path)
else if HANDLE_PROP_STRING("video-codec", videoCodec)
else if HANDLE_PROP_STRING("video-format", videoFormat)
else if HANDLE_PROP_STRING("video-params/pixelformat", videoParamsPixelformat)
else if HANDLE_PROP_STRING("video-out-params/pixelformat", videoOutParamsPixelformat)
else if HANDLE_PROP_STRING("ytdl-format", ytdlFormat)
} else if (prop->format == MPV_FORMAT_INT64) {
if HANDLE_PROP_INT("chapter", chapter)
else if HANDLE_PROP_INT("chapter-list/count", chapterListCount)
else if HANDLE_PROP_INT("decoder-frame-drop-count", decoderFrameDropCount)
else if HANDLE_PROP_INT("dwidth", dwidth)
else if HANDLE_PROP_INT("dheight", dheight)
else if HANDLE_PROP_INT("estimated-frame-count", estimatedFrameCount)
else if HANDLE_PROP_INT("estimated-frame-number", estimatedFrameNumber)
else if HANDLE_PROP_INT("frame-drop-count", frameDropCount)
else if HANDLE_PROP_INT("playlist-pos", playlistPos)
else if HANDLE_PROP_INT("playlist/count", playlistCount)
else if HANDLE_PROP_INT("vo-delayed-frame-count", voDelayedFrameCount)
else if HANDLE_PROP_INT("volume", volume)
else if HANDLE_PROP_INT("vid", vid)
else if HANDLE_PROP_INT("aid", aid)
else if HANDLE_PROP_INT("sid", sid)
else if HANDLE_PROP_INT("audio-params/channel-count", audioParamsChannelCount)
else if HANDLE_PROP_INT("audio-params/samplerate", audioParamsSampleRate)
else if HANDLE_PROP_INT("track-list/count", trackListCount)
else if HANDLE_PROP_INT("contrast", contrast)
else if HANDLE_PROP_INT("brightness", brightness)
else if HANDLE_PROP_INT("gamma", gamma)
else if HANDLE_PROP_INT("saturation", saturation)
else if HANDLE_PROP_INT("sub-margin-y", subMarginY)
} else if (prop->format == MPV_FORMAT_NODE_MAP) {
if HANDLE_PROP_MAP("demuxer-cache-state", demuxerCacheState)
}
break;
}
default: ;
// Ignore uninteresting or unknown events.
}
}
void MpvObject::play()
{
// qDebug() << "play";
if (idleActive() && playlistCount() >= 1) { // File has finished playing.
// qDebug() << "\treload";
set_playlistPos(playlistPos()); // Reload and play file again.
}
if (!isPlaying()) {
// qDebug() << "\t!isPlaying";
set_paused(false);
}
}
void MpvObject::pause()
{
// qDebug() << "pause";
if (isPlaying()) {
qDebug() << "!isPlaying";
set_paused(true);
}
}
void MpvObject::playPause()
{
if (isPlaying()) {
pause();
} else {
play();
}
}
void MpvObject::stop()
{
command(QVariantList() << "stop");
}
void MpvObject::quit()
{
command(QVariantList() << "quit");
qDebug() << "We quit mpv";
}
void MpvObject::stepBackward()
{
command(QVariantList() << "frame-back-step");
}
void MpvObject::stepForward()
{
command(QVariantList() << "frame-step");
}
void MpvObject::seek(double pos)
{
// qDebug() << "seek" << pos;
pos = qMax(0.0, qMin(pos, m_duration));
commandAsync(QVariantList() << "seek" << pos << "absolute");
}
void MpvObject::loadFile(QVariant urls)
{
qDebug() << "Url being loaded: " << urls;
command(QVariantList() << "loadfile" << urls);
}
void MpvObject::screenshotToFile(QUrl url) {
qDebug() << "Url of screenshot to be taken: " << url;
QDir dir = writeDir.absolutePath() + "/presenter/Church Presenter/thumbnails";
qDebug() << "thumbnails dir: " << dir;
QDir absDir = writeDir.absolutePath() + "/presenter/Church Presenter";
if (!dir.exists())
absDir.mkdir("thumbnails");
QString file = url.path() + ".jpg";
commandAsync(QVariantList() << "screenshot-to-file" << file << "video");
}
void MpvObject::subAdd(QVariant urls)
{
command(QVariantList() << "sub-add" << urls);
}
void MpvObject::updateState()
{
bool isNowPlaying = !idleActive() && !paused();
if (m_isPlaying != isNowPlaying) {
m_isPlaying = isNowPlaying;
emit isPlayingChanged(m_isPlaying);
}
}

205
src/cpp/mpv/mpvobject.h Normal file
View file

@ -0,0 +1,205 @@
#pragma once
#include "mpvhelpers.h"
// Qt
#include <QtQuick/QQuickItem>
#include <QtQuick/QQuickFramebufferObject>
// libmpv
#include <mpv/client.h>
#include <mpv/render_gl.h>
// own
#include "qthelper.hpp"
class MpvObject;
class MpvRenderer;
class MpvRenderer : public QQuickFramebufferObject::Renderer
{
static void* get_proc_address(void *ctx, const char *name);
public:
MpvRenderer(const MpvObject *obj);
virtual ~MpvRenderer();
void render();
private:
const MpvObject *obj;
mpv_render_context *mpv_gl;
};
class MpvObject : public QQuickFramebufferObject
{
Q_OBJECT
friend class MpvRenderer;
Q_PROPERTY(bool enableAudio READ enableAudio WRITE setEnableAudio NOTIFY enableAudioChanged)
Q_PROPERTY(bool useHwdec READ useHwdec WRITE setUseHwdec NOTIFY useHwdecChanged)
READONLY_PROP_BOOL("idle-active", idleActive)
WRITABLE_PROP_BOOL("mute", muted)
WRITABLE_PROP_BOOL("pause", paused)
READONLY_PROP_BOOL("paused-for-cache", pausedForCache)
READONLY_PROP_BOOL("seekable", seekable)
READONLY_PROP_INT("chapter", chapter)
READONLY_PROP_INT("chapter-list/count", chapterListCount) // OR "chapters"
READONLY_PROP_INT("decoder-frame-drop-count", decoderFrameDropCount)
READONLY_PROP_INT("dheight", dheight)
READONLY_PROP_INT("dwidth", dwidth)
READONLY_PROP_INT("estimated-frame-count", estimatedFrameCount)
READONLY_PROP_INT("estimated-frame-number", estimatedFrameNumber)
READONLY_PROP_INT("frame-drop-count", frameDropCount)
WRITABLE_PROP_INT("playlist-pos", playlistPos)
READONLY_PROP_INT("playlist/count", playlistCount)
WRITABLE_PROP_INT("vo-delayed-frame-count", voDelayedFrameCount)
WRITABLE_PROP_INT("volume", volume)
WRITABLE_PROP_INT("contrast", contrast)
WRITABLE_PROP_INT("brightness", brightness)
WRITABLE_PROP_INT("gamma", gamma)
WRITABLE_PROP_INT("saturation", saturation)
WRITABLE_PROP_INT("sub-margin-y", subMarginY)
READONLY_PROP_INT("vid", vid)
READONLY_PROP_INT("aid", aid)
READONLY_PROP_INT("sid", sid)
READONLY_PROP_INT("audio-params/channel-count", audioParamsChannelCount)
READONLY_PROP_INT("audio-params/samplerate", audioParamsSampleRate)
READONLY_PROP_INT("track-list/count", trackListCount)
READONLY_PROP_DOUBLE("audio-bitrate", audioBitrate)
READONLY_PROP_DOUBLE("avsync", avsync)
READONLY_PROP_DOUBLE("container-fps", containerFps)
READONLY_PROP_DOUBLE("demuxer-cache-duration", demuxerCacheDuration)
READONLY_PROP_DOUBLE("display-fps", displayFps)
READONLY_PROP_DOUBLE("estimated-display-fps", estimatedDisplayFps)
READONLY_PROP_DOUBLE("estimated-vf-fps", estimatedVfFps)
READONLY_PROP_DOUBLE("fps", fps) // Deprecated, use "container-fps"
WRITABLE_PROP_DOUBLE("speed", speed)
READONLY_PROP_DOUBLE("video-bitrate", videoBitrate)
READONLY_PROP_DOUBLE("video-params/aspect", videoParamsAspect)
READONLY_PROP_DOUBLE("video-out-params/aspect", videoOutParamsAspect)
WRITABLE_PROP_DOUBLE("window-scale", windowScale)
READONLY_PROP_DOUBLE("current-window-scale", currentWindowScale)
READONLY_PROP_STRING("audio-params/format", audioParamsFormat)
READONLY_PROP_STRING("audio-codec", audioCodec)
READONLY_PROP_STRING("audio-codec-name", audioCodecName)
READONLY_PROP_STRING("filename", filename)
READONLY_PROP_STRING("file-format", fileFormat)
READONLY_PROP_STRING("file-size", fileSize)
READONLY_PROP_STRING("audio-format", audioFormat)
WRITABLE_PROP_STRING("hwdec", hwdec)
READONLY_PROP_STRING("hwdec-current", hwdecCurrent)
READONLY_PROP_STRING("hwdec-interop", hwdecInterop)
WRITABLE_PROP_STRING("loop", loop)
READONLY_PROP_STRING("media-title", mediaTitle)
READONLY_PROP_STRING("path", path)
READONLY_PROP_STRING("video-codec", videoCodec)
READONLY_PROP_STRING("video-format", videoFormat)
READONLY_PROP_STRING("video-params/pixelformat", videoParamsPixelformat)
READONLY_PROP_STRING("video-out-params/pixelformat", videoOutParamsPixelformat)
READONLY_PROP_STRING("ytdl-format", ytdlFormat)
READONLY_PROP_MAP("demuxer-cache-state", demuxerCacheState)
public:
Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY isPlayingChanged)
Q_PROPERTY(double duration READ duration NOTIFY durationChanged)
Q_PROPERTY(double position READ position NOTIFY positionChanged)
public:
MpvObject(QQuickItem *parent = nullptr);
virtual ~MpvObject();
virtual Renderer *createRenderer() const;
Q_INVOKABLE void setProperty(const QString& name, const QVariant& value);
Q_INVOKABLE QVariant getProperty(const QString& name) const;
Q_INVOKABLE void setOption(const QString& name, const QVariant& value);
Q_INVOKABLE QString getPlaylistFilename(int playlistIndex) const { return getProperty(QString("playlist/%1/filename").arg(playlistIndex)).toString(); }
Q_INVOKABLE QString getPlaylistTitle(int playlistIndex) const { return getProperty(QString("playlist/%1/title").arg(playlistIndex)).toString(); }
Q_INVOKABLE QString getChapterTitle(int chapterIndex) const { return getProperty(QString("chapter-list/%1/title").arg(chapterIndex)).toString(); }
Q_INVOKABLE double getChapterTime(int chapterIndex) const { return getProperty(QString("chapter-list/%1/time").arg(chapterIndex)).toDouble(); }
public slots:
void command(const QVariant& params);
void commandAsync(const QVariant& params);
void playPause();
void play();
void pause();
void stop();
void quit();
void stepBackward();
void stepForward();
void seek(double pos);
void loadFile(QVariant urls);
void screenshotToFile(QUrl url);
void subAdd(QVariant urls);
bool enableAudio() const { return m_enableAudio; }
void setEnableAudio(bool value) {
if (m_enableAudio != value) {
m_enableAudio = value;
Q_EMIT enableAudioChanged(value);
}
if (!m_enableAudio) {
mpv::qt::set_option_variant(mpv, "ao", "null");
}
}
bool useHwdec() const { return m_useHwdec; }
void setUseHwdec(bool value) {
if (m_useHwdec != value) {
m_useHwdec = value;
if (m_useHwdec) {
mpv::qt::set_option_variant(mpv, "hwdec", "auto-copy");
} else {
mpv::qt::set_option_variant(mpv, "hwdec", "no");
}
Q_EMIT useHwdecChanged(value);
}
}
bool isPlaying() const { return m_isPlaying; }
void updateState();
double duration() const { return m_duration; }
double position() const { return m_position; }
signals:
void enableAudioChanged(bool value);
void useHwdecChanged(bool value);
void isPlayingChanged(bool value);
void durationChanged(double value); // Unit: seconds
void positionChanged(double value); // Unit: seconds
void mpvUpdated();
void logMessage(QString prefix, QString level, QString text);
void fileStarted();
void fileEnded(QString reason);
void fileLoaded();
private slots:
void onMpvEvents();
void doUpdate();
protected:
mpv_handle *mpv;
private:
void logPropChange(mpv_event_property *prop);
void handle_mpv_event(mpv_event *event);
static void on_update(void *ctx);
bool m_enableAudio;
bool m_useHwdec;
double m_duration;
double m_position;
bool m_isPlaying;
};

370
src/cpp/mpv/qthelper.hpp Normal file
View file

@ -0,0 +1,370 @@
#pragma once
// libmpv
#include <mpv/client.h>
// std
#include <cstring>
// Qt
#include <QVariant>
#include <QString>
#include <QList>
#include <QHash>
#include <QSharedPointer>
#include <QMetaType>
namespace mpv {
namespace qt {
// Wrapper around mpv_handle. Does refcounting under the hood.
class Handle
{
struct container {
container(mpv_handle *h) : mpv(h) {}
~container() { mpv_terminate_destroy(mpv); }
mpv_handle *mpv;
};
QSharedPointer<container> sptr;
public:
// Construct a new Handle from a raw mpv_handle with refcount 1. If the
// last Handle goes out of scope, the mpv_handle will be destroyed with
// mpv_terminate_destroy().
// Never destroy the mpv_handle manually when using this wrapper. You
// will create dangling pointers. Just let the wrapper take care of
// destroying the mpv_handle.
// Never create multiple wrappers from the same raw mpv_handle; copy the
// wrapper instead (that's what it's for).
static Handle FromRawHandle(mpv_handle *handle) {
Handle h;
h.sptr = QSharedPointer<container>(new container(handle));
return h;
}
// Return the raw handle; for use with the libmpv C API.
operator mpv_handle*() const { return sptr ? (*sptr).mpv : 0; }
};
static inline QVariant node_to_variant(const mpv_node *node)
{
switch (node->format) {
case MPV_FORMAT_STRING:
return QVariant(QString::fromUtf8(node->u.string));
case MPV_FORMAT_FLAG:
return QVariant(static_cast<bool>(node->u.flag));
case MPV_FORMAT_INT64:
return QVariant(static_cast<qlonglong>(node->u.int64));
case MPV_FORMAT_DOUBLE:
return QVariant(node->u.double_);
case MPV_FORMAT_NODE_ARRAY: {
mpv_node_list *list = node->u.list;
QVariantList qlist;
for (int n = 0; n < list->num; n++)
qlist.append(node_to_variant(&list->values[n]));
return QVariant(qlist);
}
case MPV_FORMAT_NODE_MAP: {
mpv_node_list *list = node->u.list;
QVariantMap qmap;
for (int n = 0; n < list->num; n++) {
qmap.insert(QString::fromUtf8(list->keys[n]),
node_to_variant(&list->values[n]));
}
return QVariant(qmap);
}
default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions)
return QVariant();
}
}
struct node_builder {
node_builder(const QVariant& v) {
set(&node_, v);
}
~node_builder() {
free_node(&node_);
}
mpv_node *node() { return &node_; }
private:
Q_DISABLE_COPY(node_builder)
mpv_node node_;
mpv_node_list *create_list(mpv_node *dst, bool is_map, int num) {
dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY;
mpv_node_list *list = new mpv_node_list();
dst->u.list = list;
if (!list)
goto err;
list->values = new mpv_node[num]();
if (!list->values)
goto err;
if (is_map) {
list->keys = new char*[num]();
if (!list->keys)
goto err;
}
return list;
err:
free_node(dst);
return NULL;
}
char *dup_qstring(const QString &s) {
QByteArray b = s.toUtf8();
char *r = new char[b.size() + 1];
if (r)
std::memcpy(r, b.data(), b.size() + 1);
return r;
}
bool test_type(const QVariant &v, QMetaType::Type t) {
// The Qt docs say: "Although this function is declared as returning
// "QVariant::Type(obsolete), the return value should be interpreted
// as QMetaType::Type."
// So a cast really seems to be needed to avoid warnings (urgh).
return static_cast<int>(v.type()) == static_cast<int>(t);
}
void set(mpv_node *dst, const QVariant &src) {
if (test_type(src, QMetaType::QString)) {
dst->format = MPV_FORMAT_STRING;
dst->u.string = dup_qstring(src.toString());
if (!dst->u.string)
goto fail;
} else if (test_type(src, QMetaType::Bool)) {
dst->format = MPV_FORMAT_FLAG;
dst->u.flag = src.toBool() ? 1 : 0;
} else if (test_type(src, QMetaType::Int) ||
test_type(src, QMetaType::LongLong) ||
test_type(src, QMetaType::UInt) ||
test_type(src, QMetaType::ULongLong))
{
dst->format = MPV_FORMAT_INT64;
dst->u.int64 = src.toLongLong();
} else if (test_type(src, QMetaType::Double)) {
dst->format = MPV_FORMAT_DOUBLE;
dst->u.double_ = src.toDouble();
} else if (src.canConvert<QVariantList>()) {
QVariantList qlist = src.toList();
mpv_node_list *list = create_list(dst, false, qlist.size());
if (!list)
goto fail;
list->num = qlist.size();
for (int n = 0; n < qlist.size(); n++)
set(&list->values[n], qlist[n]);
} else if (src.canConvert<QVariantMap>()) {
QVariantMap qmap = src.toMap();
mpv_node_list *list = create_list(dst, true, qmap.size());
if (!list)
goto fail;
list->num = qmap.size();
for (int n = 0; n < qmap.size(); n++) {
list->keys[n] = dup_qstring(qmap.keys()[n]);
if (!list->keys[n]) {
free_node(dst);
goto fail;
}
set(&list->values[n], qmap.values()[n]);
}
} else {
goto fail;
}
return;
fail:
dst->format = MPV_FORMAT_NONE;
}
void free_node(mpv_node *dst) {
switch (dst->format) {
case MPV_FORMAT_STRING:
delete[] dst->u.string;
break;
case MPV_FORMAT_NODE_ARRAY:
case MPV_FORMAT_NODE_MAP: {
mpv_node_list *list = dst->u.list;
if (list) {
for (int n = 0; n < list->num; n++) {
if (list->keys)
delete[] list->keys[n];
if (list->values)
free_node(&list->values[n]);
}
delete[] list->keys;
delete[] list->values;
}
delete list;
break;
}
default: ;
}
dst->format = MPV_FORMAT_NONE;
}
};
/**
* RAII wrapper that calls mpv_free_node_contents() on the pointer.
*/
struct node_autofree {
mpv_node *ptr;
node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {}
~node_autofree() { mpv_free_node_contents(ptr); }
};
/**
* Return the given property as mpv_node converted to QVariant, or QVariant()
* on error.
*
* @deprecated use get_property() instead
*
* @param name the property name
*/
static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name)
{
mpv_node node;
if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0)
return QVariant();
node_autofree f(&node);
return node_to_variant(&node);
}
/**
* Set the given property as mpv_node converted from the QVariant argument.
* @deprecated use set_property() instead
*/
static inline int set_property_variant(mpv_handle *ctx, const QString &name,
const QVariant &v)
{
node_builder node(v);
return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
}
/**
* Set the given option as mpv_node converted from the QVariant argument.
*
* @deprecated use set_property() instead
*/
static inline int set_option_variant(mpv_handle *ctx, const QString &name,
const QVariant &v)
{
node_builder node(v);
return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
}
/**
* mpv_command_node() equivalent. Returns QVariant() on error (and
* unfortunately, the same on success).
*
* @deprecated use command() instead
*/
static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
{
node_builder node(args);
mpv_node res;
if (mpv_command_node(ctx, node.node(), &res) < 0)
return QVariant();
node_autofree f(&res);
return node_to_variant(&res);
}
/**
* This is used to return error codes wrapped in QVariant for functions which
* return QVariant.
*
* You can use get_error() or is_error() to extract the error status from a
* QVariant value.
*/
struct ErrorReturn
{
/**
* enum mpv_error value (or a value outside of it if ABI was extended)
*/
int error;
ErrorReturn() : error(0) {}
explicit ErrorReturn(int err) : error(err) {}
};
/**
* Return the mpv error code packed into a QVariant, or 0 (success) if it's not
* an error value.
*
* @return error code (<0) or success (>=0)
*/
static inline int get_error(const QVariant &v)
{
if (!v.canConvert<ErrorReturn>())
return 0;
return v.value<ErrorReturn>().error;
}
/**
* Return whether the QVariant carries a mpv error code.
*/
static inline bool is_error(const QVariant &v)
{
return get_error(v) < 0;
}
/**
* Return the given property as mpv_node converted to QVariant, or QVariant()
* on error.
*
* @param name the property name
* @return the property value, or an ErrorReturn with the error code
*/
static inline QVariant get_property(mpv_handle *ctx, const QString &name)
{
mpv_node node;
int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node);
if (err < 0)
return QVariant::fromValue(ErrorReturn(err));
node_autofree f(&node);
return node_to_variant(&node);
}
/**
* Set the given property as mpv_node converted from the QVariant argument.
*
* @return mpv error code (<0 on error, >= 0 on success)
*/
static inline int set_property(mpv_handle *ctx, const QString &name,
const QVariant &v)
{
node_builder node(v);
return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
}
/**
* mpv_command_node() equivalent.
*
* @param args command arguments, with args[0] being the command name as string
* @return the property value, or an ErrorReturn with the error code
*/
static inline QVariant command(mpv_handle *ctx, const QVariant &args)
{
node_builder node(args);
mpv_node res;
int err = mpv_command_node(ctx, node.node(), &res);
if (err < 0)
return QVariant::fromValue(ErrorReturn(err));
node_autofree f(&res);
return node_to_variant(&res);
}
/**
* mpv_command_node_async() equivalent.
*
* @param args command arguments, with args[0] being the command name as string
* @return empty QVariant, or an ErrorReturn with the error code
*/
static inline QVariant command_async(mpv_handle *ctx, const QVariant &args)
{
node_builder node(args);
quint64 replyUserdata = 0; // TODO: Bomi casted args[0] to int. Bomi's args was a QByteArray however.
int err = mpv_command_node_async(ctx, replyUserdata, node.node());
if (err < 0)
return QVariant::fromValue(ErrorReturn(err));
// TODO: Return unique replyUserdata so the app can wait for
// the result in an MPV_EVENT_COMMAND_REPLY event.
return QVariant();
}
}
}
Q_DECLARE_METATYPE(mpv::qt::ErrorReturn)

View file

@ -0,0 +1,203 @@
#include "presentationsqlmodel.h"
#include <QDateTime>
#include <QDebug>
#include <QSqlError>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QSql>
// #include <QPdfDocument>
#include <QSqlDatabase>
#include <QFileInfo>
#include <qabstractitemmodel.h>
#include <qdebug.h>
#include <qnamespace.h>
#include <qobject.h>
#include <qobjectdefs.h>
#include <qsqlrecord.h>
#include <qurl.h>
#include <qvariant.h>
static const char *presentationsTableName = "presentations";
static void createPresentationTable()
{
QSqlQuery query;
if(QSqlDatabase::database().tables().contains(presentationsTableName)) {
// query.exec("DROP TABLE 'presentations'");
// qDebug() << query.lastQuery();
return;
}
if (!query.exec("CREATE TABLE IF NOT EXISTS 'presentations' ("
" 'id' INTEGER NOT NULL,"
" 'title' TEXT NOT NULL,"
" 'filePath' TEXT NOT NULL,"
" PRIMARY KEY(id))")) {
qFatal("Failed to query database: %s",
qPrintable(query.lastError().text()));
}
qDebug() << query.lastQuery();
qDebug() << "inserting into presentations";
query.exec("INSERT INTO presentations (title, filePath) VALUES ('Dec 180', 'file:///home/chris/nextcloud/tfc/openlp/5 slides-1.pdf')");
qDebug() << query.lastQuery();
query.exec("INSERT INTO presentations (title, filePath) VALUES ('No TFC', "
"'file:///home/chris/nextcloud/tfc/openlp/5 slides-2.pdf')");
query.exec("select * from presentations");
qDebug() << query.lastQuery();
}
PresentationSqlModel::PresentationSqlModel(QObject *parent) : QSqlTableModel(parent) {
qDebug() << "creating presentation table";
createPresentationTable();
setTable(presentationsTableName);
setEditStrategy(QSqlTableModel::OnManualSubmit);
// make sure to call select else the model won't fill
select();
}
QVariant PresentationSqlModel::data(const QModelIndex &index, int role) const {
if (role < Qt::UserRole) {
return QSqlTableModel::data(index, role);
}
// qDebug() << role;
const QSqlRecord sqlRecord = record(index.row());
return sqlRecord.value(role - Qt::UserRole);
}
QHash<int, QByteArray> PresentationSqlModel::roleNames() const
{
QHash<int, QByteArray> names;
names[Qt::UserRole] = "id";
names[Qt::UserRole + 1] = "title";
names[Qt::UserRole + 2] = "filePath";
return names;
}
void PresentationSqlModel::newPresentation(const QUrl &filePath) {
qDebug() << "adding new presentation";
int rows = rowCount();
qDebug() << rows;
QSqlRecord recordData = record();
QFileInfo fileInfo = filePath.toString();
QString title = fileInfo.baseName();
recordData.setValue("title", title);
recordData.setValue("filePath", filePath);
if (insertRecord(rows, recordData)) {
submitAll();
} else {
qDebug() << lastError();
};
}
void PresentationSqlModel::deletePresentation(const int &row) {
QSqlRecord recordData = record(row);
if (recordData.isEmpty())
return;
removeRow(row);
submitAll();
}
int PresentationSqlModel::id() const {
return m_id;
}
QString PresentationSqlModel::title() const {
return m_title;
}
void PresentationSqlModel::setTitle(const QString &title) {
if (title == m_title)
return;
m_title = title;
select();
emit titleChanged();
}
// This function is for updating the title from outside the delegate
void PresentationSqlModel::updateTitle(const int &row, const QString &title) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from presentations");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("title", title);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit titleChanged();
}
QUrl PresentationSqlModel::filePath() const {
return m_filePath;
}
void PresentationSqlModel::setFilePath(const QUrl &filePath) {
if (filePath == m_filePath)
return;
m_filePath = filePath;
select();
emit filePathChanged();
}
// This function is for updating the filepath from outside the delegate
void PresentationSqlModel::updateFilePath(const int &row, const QUrl &filePath) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from presentations");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("filePath", filePath);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit filePathChanged();
}
QVariantMap PresentationSqlModel::getPresentation(const int &row) {
// qDebug() << "Row we are getting is " << row;
// QUrl presentation;
// QSqlRecord rec = record(row);
// qDebug() << rec.value("filePath").toUrl();
// // presentation.append(rec.value("title"));
// // presentation.append(rec.value("filePath"));
// presentation = rec.value("filePath").toUrl();
// return presentation;
QVariantMap data;
const QModelIndex idx = this->index(row,0);
// qDebug() << idx;
if( !idx.isValid() )
return data;
const QHash<int,QByteArray> rn = roleNames();
// qDebug() << rn;
QHashIterator<int,QByteArray> it(rn);
while (it.hasNext()) {
it.next();
qDebug() << it.key() << ":" << it.value();
data[it.value()] = idx.data(it.key());
}
return data;
}

View file

@ -0,0 +1,49 @@
#ifndef PRESENTATIONSQLMODEL_H
#define PRESENTATIONSQLMODEL_H
#include <QSqlTableModel>
#include <qobject.h>
#include <qobjectdefs.h>
#include <qqml.h>
#include <qurl.h>
#include <qvariant.h>
class PresentationSqlModel : public QSqlTableModel
{
Q_OBJECT
Q_PROPERTY(int id READ id)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QUrl filePath READ filePath WRITE setFilePath NOTIFY filePathChanged)
QML_ELEMENT
public:
PresentationSqlModel(QObject *parent = 0);
int id() const;
QString title() const;
QUrl filePath() const;
void setTitle(const QString &title);
void setFilePath(const QUrl &filePath);
Q_INVOKABLE void updateTitle(const int &row, const QString &title);
Q_INVOKABLE void updateFilePath(const int &row, const QUrl &filePath);
Q_INVOKABLE void newPresentation(const QUrl &filePath);
Q_INVOKABLE void deletePresentation(const int &row);
Q_INVOKABLE QVariantMap getPresentation(const int &row);
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
signals:
void titleChanged();
void filePathChanged();
private:
int m_id;
QString m_title;
QUrl m_filePath;
};
#endif //PRESENTATIONSQLMODEL_H

186
src/cpp/serviceitem.cpp Normal file
View file

@ -0,0 +1,186 @@
#include "serviceitem.h"
#include <QDebug>
ServiceItem::ServiceItem(QObject *parent)
: QObject{parent}
{
}
ServiceItem::ServiceItem(const QString &name, const QString &type, QObject *parent)
: QObject(parent),m_name(name),m_type(type)
{
}
ServiceItem::ServiceItem(const QString &name, const QString &type, const QString &background,
const QString &backgroundType, QObject *parent)
: QObject(parent),m_name(name),m_type(type),m_background(background),
m_backgroundType(backgroundType)
{
}
ServiceItem::ServiceItem(const QString &name, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
QObject *parent)
: QObject(parent),m_name(name),m_type(type),m_background(background),
m_backgroundType(backgroundType),m_text(text)
{
}
ServiceItem::ServiceItem(const QString &name, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
const QString &audio, QObject *parent)
: QObject(parent),m_name(name),m_type(type),m_background(background),
m_backgroundType(backgroundType),m_text(text),m_audio(audio)
{
}
ServiceItem::ServiceItem(const QString &name, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
const QString &audio, const QString &font, const int &fontSize,
QObject *parent)
: QObject(parent),m_name(name),m_type(type),m_background(background),
m_backgroundType(backgroundType),m_text(text),m_audio(audio),m_font(font),m_fontSize(fontSize)
{
}
QString ServiceItem::name() const {
return m_name;
}
QString ServiceItem::type() const {
return m_type;
}
QString ServiceItem::background() const
{
return m_background;
}
QString ServiceItem::backgroundType() const
{
return m_backgroundType;
}
QStringList ServiceItem::text() const
{
return m_text;
}
QString ServiceItem::audio() const {
return m_audio;
}
QString ServiceItem::font() const {
return m_font;
}
int ServiceItem::fontSize() const {
return m_fontSize;
}
bool ServiceItem::active() const {
return m_active;
}
bool ServiceItem::selected() const {
return m_selected;
}
void ServiceItem::setName(QString name)
{
if (m_name == name)
return;
m_name = name;
emit nameChanged(m_name);
}
void ServiceItem::setType(QString type)
{
if (m_type == type)
return;
m_type = type;
emit typeChanged(m_type);
}
void ServiceItem::setBackground(QString background)
{
if (m_background == background)
return;
m_background = background;
emit backgroundChanged(m_background);
}
void ServiceItem::setBackgroundType(QString backgroundType)
{
if (m_backgroundType == backgroundType)
return;
m_backgroundType = backgroundType;
emit backgroundTypeChanged(m_backgroundType);
}
void ServiceItem::setText(QStringList text)
{
if (m_text == text)
return;
m_text = text;
emit textChanged(m_text);
}
void ServiceItem::setAudio(QString audio)
{
if (m_audio == audio)
return;
m_audio = audio;
emit audioChanged(m_audio);
}
void ServiceItem::setFont(QString font)
{
if (m_font == font)
return;
m_font = font;
emit fontChanged(m_font);
}
void ServiceItem::setFontSize(int fontSize)
{
if (m_fontSize == fontSize)
return;
m_fontSize = fontSize;
emit fontSizeChanged(m_fontSize);
}
void ServiceItem::setActive(bool active)
{
qDebug() << "::::::::::::::::::::";
qDebug() << "CHANGE ME!";
if (m_active == active)
return;
m_active = active;
emit activeChanged(m_active);
}
void ServiceItem::setSelected(bool selected)
{
if (m_selected == selected)
return;
m_selected = selected;
emit selectedChanged(m_selected);
}

86
src/cpp/serviceitem.h Normal file
View file

@ -0,0 +1,86 @@
#ifndef SERVICEITEM_H
#define SERVICEITEM_H
#include <QObject>
#include <qobject.h>
#include "thumbnail.h"
class ServiceItem : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString type READ type WRITE setType NOTIFY typeChanged)
Q_PROPERTY(QString background READ background WRITE setBackground NOTIFY backgroundChanged)
Q_PROPERTY(QString backgroundType READ backgroundType WRITE setBackgroundType NOTIFY backgroundTypeChanged)
Q_PROPERTY(QStringList text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(QString audio READ audio WRITE setAudio NOTIFY audioChanged)
Q_PROPERTY(QString font READ font WRITE setFont NOTIFY fontChanged)
Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged)
// Q_PROPERTY(Thumbnail thumbnail READ thumbnail WRITE setThumbnail NOTIFY thumbnailChanged)
public:
explicit ServiceItem(QObject *parent = nullptr);
ServiceItem(const QString &name, const QString &type, QObject * parent = nullptr);
ServiceItem(const QString &name, const QString &type, const QString &background,
const QString &backgroundType, QObject * parent = nullptr);
ServiceItem(const QString &name, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
QObject * parent = nullptr);
ServiceItem(const QString &name, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text, const QString &audio,
QObject * parent = nullptr);
ServiceItem(const QString &name, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text, const QString &audio,
const QString &font, const int &fontSize, QObject * parent = nullptr);
QString name() const;
QString type() const;
QString background() const;
QString backgroundType() const;
QStringList text() const;
QString audio() const;
QString font() const;
int fontSize() const;
bool active() const;
bool selected() const;
// Thumbnail thumbnail() const;
void setName(QString name);
void setType(QString type);
void setBackground(QString background);
void setBackgroundType(QString backgroundType);
void setText(QStringList text);
void setAudio(QString audio);
void setFont(QString font);
void setFontSize(int fontSize);
void setActive(bool active);
void setSelected(bool selected);
signals:
void nameChanged(QString name);
void typeChanged(QString type);
void backgroundChanged(QString background);
void backgroundTypeChanged(QString backgroundType);
void textChanged(QStringList text);
void audioChanged(QString audio);
void fontChanged(QString font);
void fontSizeChanged(int fontSize);
void activeChanged(bool active);
void selectedChanged(bool selected);
private:
QString m_name;
QString m_type;
QString m_background;
QString m_backgroundType;
QStringList m_text;
QString m_audio;
QString m_font;
int m_fontSize;
bool m_active;
bool m_selected;
};
#endif // SERVICEITEM_H

View file

@ -0,0 +1,737 @@
#include "serviceitemmodel.h"
#include "serviceitem.h"
#include "filemanager.h"
#include <qabstractitemmodel.h>
#include <qglobal.h>
#include <qnamespace.h>
#include <qvariant.h>
#include <ktar.h>
#include <KCompressionDevice>
#include <KArchiveDirectory>
#include <KArchiveFile>
#include <KArchiveEntry>
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QFile>
#include <QMap>
#include <QTemporaryFile>
#include <QDir>
#include <QUrl>
#include <QSettings>
#include <QStandardPaths>
#include <QImage>
ServiceItemModel::ServiceItemModel(QObject *parent)
: QAbstractListModel(parent) {
if (!loadLastSaved()) {
addItem(new ServiceItem("10,000 Reasons", "song",
"file:/home/chris/nextcloud/tfc/openlp/CMG - Nature King 21.jpg",
"image", QStringList("Yip Yip"),
"file:/home/chris/nextcloud/tfc/openlp/music/Eden-Phil Wickham [lyrics].mp3"));
addItem(new ServiceItem("Marvelous Light", "song",
"file:/home/chris/nextcloud/tfc/openlp/Fire Embers_Loop.mp4",
"video", QStringList("Hallelujah!")));
addItem(new ServiceItem("BP Text", "video",
"file:/home/chris/nextcloud/tfc/openlp/videos/test.mp4",
"video", QStringList()));
}
}
int ServiceItemModel::rowCount(const QModelIndex &parent) const {
// For list models only the root node (an invalid parent) should return the
// list's size. For all other (valid) parents, rowCount() should return 0 so
// that it does not become a tree model.
if (parent.isValid())
return 0;
// FIXME: Implement me!
return m_items.size();
}
QVariant ServiceItemModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
ServiceItem *item = m_items[index.row()];
switch (role) {
case NameRole:
return item->name();
case TypeRole:
return item->type();
case BackgroundRole:
return item->background();
case BackgroundTypeRole:
return item->backgroundType();
case TextRole:
return item->text();
case AudioRole:
return item->audio();
case FontRole:
return item->font();
case FontSizeRole:
return item->fontSize();
case ActiveRole:
return item->active();
case SelectedRole:
return item->selected();
default:
return QVariant();
}
}
QHash<int, QByteArray> ServiceItemModel::roleNames() const {
static QHash<int, QByteArray> mapping{{NameRole, "name"},
{TypeRole, "type"},
{BackgroundRole, "background"},
{BackgroundTypeRole, "backgroundType"},
{TextRole, "text"},
{AudioRole, "audio"},
{FontRole, "font"},
{FontSizeRole, "fontSize"},
{ActiveRole, "active"},
{SelectedRole, "selected"}};
return mapping;
}
bool ServiceItemModel::setData(const QModelIndex &index, const QVariant &value,
int role) {
ServiceItem *item = m_items[index.row()];
bool somethingChanged = false;
switch (role) {
case NameRole:
if (item->name() != value.toString()) {
item->setName(value.toString());
somethingChanged = true;
}
break;
case TypeRole:
if (item->type() != value.toString()) {
item->setType(value.toString());
somethingChanged = true;
}
break;
case BackgroundRole:
if (item->background() != value.toString()) {
item->setBackground(value.toString());
somethingChanged = true;
}
break;
case BackgroundTypeRole:
if (item->backgroundType() != value.toString()) {
item->setBackgroundType(value.toString());
somethingChanged = true;
}
break;
case TextRole:
if (item->text() != value.toStringList()) {
item->setText(value.toStringList());
somethingChanged = true;
}
break;
case AudioRole:
if (item->audio() != value.toString()) {
item->setAudio(value.toString());
somethingChanged = true;
}
break;
case FontRole:
if (item->font() != value.toString()) {
item->setFont(value.toString());
somethingChanged = true;
}
break;
case FontSizeRole:
if (item->fontSize() != value.toInt()) {
item->setFontSize(value.toInt());
somethingChanged = true;
}
break;
case ActiveRole:
if (item->active() != value.toBool()) {
item->setActive(value.toBool());
somethingChanged = true;
}
break;
case SelectedRole:
if (item->selected() != value.toBool()) {
item->setSelected(value.toBool());
somethingChanged = true;
}
break;
if (somethingChanged) {
emit dataChanged(index, index, QVector<int>() << role);
return true;
}
}
return false;
}
Qt::ItemFlags ServiceItemModel::flags(const QModelIndex &index) const {
if (!index.isValid())
return Qt::NoItemFlags;
return Qt::ItemIsEditable; // FIXME: Implement me!
}
// int ServiceItemModel::index(int row, int column, const QModelIndex &parent) {
// if (!hasIndex(row, column, parent))
// return QModelIndex();
// ServiceItem *parentItem;
// if (!parent.isValid())
// parentItem = rootItem;
// else
// parentItem = static_cast<ServiceItem*>(parent.internalPointer());
// ServiceItem *childItem = parentItem->child(row);
// if (childItem)
// return createIndex(row, column, childItem);
// return QModelIndex();
// }
void ServiceItemModel::addItem(ServiceItem *item) {
const int index = m_items.size();
qDebug() << index;
// foreach (item, m_items) {
// qDebug() << item;
// }
beginInsertRows(QModelIndex(), index, index);
m_items.append(item);
endInsertRows();
}
void ServiceItemModel::insertItem(const int &index, ServiceItem *item) {
beginInsertRows(this->index(index).parent(), index, index);
m_items.insert(index, item);
endInsertRows();
qDebug() << "Success";
}
void ServiceItemModel::addItem(const QString &name, const QString &type) {
ServiceItem *item = new ServiceItem(name, type);
item->setSelected(false);
item->setActive(false);
addItem(item);
}
void ServiceItemModel::addItem(const QString &name, const QString &type,
const QString &background, const QString &backgroundType) {
ServiceItem *item = new ServiceItem(name, type, background, backgroundType);
item->setSelected(false);
item->setActive(false);
addItem(item);
}
void ServiceItemModel::addItem(const QString &name, const QString &type,
const QString &background, const QString &backgroundType,
const QStringList &text) {
ServiceItem *item = new ServiceItem(name, type, background, backgroundType, text);
item->setSelected(false);
item->setActive(false);
addItem(item);
qDebug() << name << type << background;
}
void ServiceItemModel::addItem(const QString &name, const QString &type,
const QString &background, const QString &backgroundType,
const QStringList &text, const QString &audio) {
ServiceItem *item = new ServiceItem(name, type, background, backgroundType,
text, audio);
item->setSelected(false);
item->setActive(false);
addItem(item);
qDebug() << name << type << background;
}
void ServiceItemModel::addItem(const QString &name, const QString &type,
const QString &background, const QString &backgroundType,
const QStringList &text, const QString &audio,
const QString &font, const int &fontSize) {
ServiceItem *item = new ServiceItem(name, type, background, backgroundType,
text, audio, font, fontSize);
item->setSelected(false);
item->setActive(false);
addItem(item);
qDebug() << "#################################";
qDebug() << name << type << font << fontSize;
qDebug() << "#################################";
}
void ServiceItemModel::insertItem(const int &index, const QString &name, const QString &type) {
ServiceItem *item = new ServiceItem(name, type);
item->setSelected(false);
item->setActive(false);
insertItem(index, item);
qDebug() << name << type;
}
void ServiceItemModel::insertItem(const int &index, const QString &name, const QString &type,
const QString &background, const QString &backgroundType) {
ServiceItem *item = new ServiceItem(name, type, background, backgroundType);
item->setSelected(false);
item->setActive(false);
insertItem(index, item);
qDebug() << name << type << background;
}
void ServiceItemModel::insertItem(const int &index, const QString &name, const QString &type,
const QString &background, const QString &backgroundType,
const QStringList &text) {
ServiceItem *item = new ServiceItem(name, type, background, backgroundType, text);
insertItem(index, item);
qDebug() << name << type << background << text;
}
void ServiceItemModel::insertItem(const int &index, const QString &name,
const QString &type,const QString &background,
const QString &backgroundType,const QStringList &text,
const QString &audio) {
ServiceItem *item = new ServiceItem(name, type, background, backgroundType,
text, audio);
item->setSelected(false);
item->setActive(false);
insertItem(index, item);
qDebug() << name << type << background << text;
}
void ServiceItemModel::insertItem(const int &index, const QString &name,
const QString &type,const QString &background,
const QString &backgroundType,const QStringList &text,
const QString &audio, const QString &font, const int &fontSize) {
ServiceItem *item = new ServiceItem(name, type, background, backgroundType,
text, audio, font, fontSize);
item->setSelected(false);
item->setActive(false);
insertItem(index, item);
qDebug() << "#################################";
qDebug() << name << type << font << fontSize;
qDebug() << "#################################";
}
void ServiceItemModel::removeItem(int index) {
beginRemoveRows(QModelIndex(), index, index);
m_items.removeAt(index);
endRemoveRows();
}
bool ServiceItemModel::moveRows(int sourceIndex, int destIndex, int count) {
qDebug() << index(sourceIndex).row();
qDebug() << index(destIndex).row();
const int lastIndex = rowCount() - 1;
if (sourceIndex == destIndex
|| (sourceIndex < 0 || sourceIndex > lastIndex)
|| (destIndex < 0 || destIndex > lastIndex)) {
return false;
}
const QModelIndex parent = index(sourceIndex).parent();
const bool isMoveDown = destIndex > sourceIndex;
if (!beginMoveRows(parent, sourceIndex, sourceIndex + count - 1,
parent, isMoveDown ? destIndex + 1 : destIndex)) {
qDebug() << "Can't move rows";
return false;
}
qDebug() << "starting move: " << "source: " << sourceIndex << "dest: " << destIndex;
m_items.move(sourceIndex, isMoveDown ? destIndex + 1 : destIndex);
endMoveRows();
return true;
}
bool ServiceItemModel::moveDown(int id) {
qDebug() << index(id).row();
qDebug() << index(id + 1).row();
QModelIndex parent = index(id).parent();
bool begsuc = beginMoveRows(parent, id,
id, parent, id + 2);
if (begsuc) {
int dest = id + 1;
if (dest >= m_items.size())
{
qDebug() << "dest too big, moving to end";
m_items.move(id, m_items.size() - 1);
}
else
m_items.move(id, dest);
endMoveRows();
return true;
}
return false;
}
bool ServiceItemModel::moveUp(int id) {
qDebug() << index(id).row();
qDebug() << index(id - 1).row();
QModelIndex parent = index(id).parent();
bool begsuc = beginMoveRows(parent, id,
id, parent, id - 1);
if (begsuc) {
int dest = id - 1;
if (dest <= -1)
{
qDebug() << "dest too big, moving to beginning";
m_items.move(id, 0);
}
else
m_items.move(id, dest);
endMoveRows();
return true;
}
return false;
}
QVariantMap ServiceItemModel::getItem(int index) const {
QVariantMap data;
const QModelIndex idx = this->index(index,0);
// qDebug() << idx;
if( !idx.isValid() )
return data;
const QHash<int,QByteArray> rn = roleNames();
// qDebug() << rn;
QHashIterator<int,QByteArray> it(rn);
while (it.hasNext()) {
it.next();
qDebug() << it.key() << ":" << it.value();
data[it.value()] = idx.data(it.key());
}
return data;
}
QVariantList ServiceItemModel::getItems() {
QVariantList data;
ServiceItem * item;
foreach (item, m_items) {
qDebug() << item->name();
QVariantMap itm;
itm["name"] = item->name();
itm["type"] = item->type();
itm["background"] = item->background();
itm["backgroundType"] = item->backgroundType();
itm["text"] = item->text();
itm["audio"] = item->audio();
itm["font"] = item->font();
itm["fontSize"] = item->fontSize();
itm["selected"] = item->selected();
itm["active"] = item->active();
data.append(itm);
}
qDebug() << "$$$$$$$$$$$$$$$$$$$$$$$$$$$";
qDebug() << data;
qDebug() << "$$$$$$$$$$$$$$$$$$$$$$$$$$$";
return data;
}
bool ServiceItemModel::select(int id) {
for (int i = 0; i < m_items.length(); i++) {
QModelIndex idx = index(i);
ServiceItem *item = m_items[idx.row()];
if (item->selected()) {
item->setSelected(false);
qDebug() << "################";
qDebug() << "deselected" << item->name();
qDebug() << "################";
emit dataChanged(idx, idx, QVector<int>() << SelectedRole);
}
}
QModelIndex idx = index(id);
ServiceItem *item = m_items[idx.row()];
item->setSelected(true);
qDebug() << "################";
qDebug() << "selected" << item->name();
qDebug() << "################";
emit dataChanged(idx, idx, QVector<int>() << SelectedRole);
return true;
}
bool ServiceItemModel::activate(int id) {
QModelIndex idx = index(id);
ServiceItem *item = m_items[idx.row()];
for (int i = 0; i < m_items.length(); i++) {
QModelIndex idx = index(i);
ServiceItem *itm = m_items[idx.row()];
if (itm->active()) {
itm->setActive(false);
qDebug() << "################";
qDebug() << "deactivated" << itm->name();
qDebug() << "################";
emit dataChanged(idx, idx, QVector<int>() << ActiveRole);
}
}
item->setActive(true);
qDebug() << "################";
qDebug() << "activated" << item->name();
qDebug() << "################";
emit dataChanged(idx, idx, QVector<int>() << ActiveRole);
return true;
}
bool ServiceItemModel::deactivate(int id) {
QModelIndex idx = index(id);
ServiceItem *item = m_items[idx.row()];
item->setActive(false);
qDebug() << "################";
qDebug() << "deactivated" << item->name();
qDebug() << "################";
emit dataChanged(idx, idx, QVector<int>() << ActiveRole);
return true;
}
bool ServiceItemModel::save(QUrl file) {
qDebug() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
qDebug() << "Saving...";
qDebug() << "File path is: " << file.toString();
qDebug() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
//first we'll get a json representation of our serviceList
//save that to a temp file in case we need it but also convert
//it to a byte array just before putting it into the archive
QJsonArray jsonData;
// save all the data and files in jsonData as just the base name
// so that they are properly mapped in the resulting archive
for (int i = 0; i < m_items.length(); i++) {
// qDebug() << serviceList[i];
QMap<QString, QVariant> item;
qDebug() << m_items[i]->name();
item.insert("name", m_items[i]->name());
item.insert("background", m_items[i]->background());
item.insert("backgroundType", m_items[i]->backgroundType());
item.insert("audio", m_items[i]->audio());
item.insert("font", m_items[i]->font());
item.insert("fontSize", m_items[i]->fontSize());
item.insert("text", m_items[i]->text());
item.insert("type", m_items[i]->type());
qDebug() << "AUDIO IS: " << item.value("audio").toString();
QFileInfo audioFile = item.value("audio").toString();
qDebug() << audioFile.fileName();
item["flatAudio"] = audioFile.fileName();
qDebug() << "AUDIO IS NOW: " << item.value("audio").toString();
QFileInfo backgroundFile = item.value("background").toString();
item["flatBackground"] = backgroundFile.fileName();
qDebug() << "BACKGRUOND IS: " << item.value("background").toString();
// qDebug() << serviceList[i].value();
QJsonObject obj = QJsonObject::fromVariantMap(item);
qDebug() << obj;
jsonData.insert(i, obj);
}
qDebug() << jsonData;
QJsonDocument jsonText(jsonData);
QTemporaryFile jsonFile;
if (!jsonFile.exists())
qDebug() << "NOT EXISTS!";
if (!jsonFile.open())
return false;
//finalize the temp json file, in case something goes wrong in the
//archive, we'll have this to jump back to
jsonFile.write(jsonText.toJson());
qDebug() << jsonFile.fileName();
jsonFile.close();
//now we create our archive file and set it's parameters
QString filename = file.toString().right(file.toString().size() - 7);
qDebug() << filename;
QString tarname;
if (filename.endsWith(".pres")) {
qDebug() << "Perfect just go with it!";
tarname = filename;
} else
tarname = filename + ".pres";
KTar tar(tarname, "application/zstd");
if (tar.open(QIODevice::WriteOnly)) {
qDebug() << tar.isOpen();
//write our json data to the archive
tar.writeFile("servicelist.json",
jsonText.toJson());
//let's add the backgrounds and audios to the archive
for (int i = 0; i < m_items.size(); i++) {
qDebug() << m_items[i]->name();
QString background = m_items[i]->background();
QString backgroundFile = background.right(background.size() - 5);
qDebug() << backgroundFile;
QString audio = m_items[i]->audio();
QString audioFile = audio.right(audio.size() - 5);
qDebug() << audioFile;
//here we need to cut off all the directories before
//adding into the archive
tar.addLocalFile(backgroundFile,
backgroundFile.right(backgroundFile.size() -
backgroundFile.lastIndexOf("/") - 1));
tar.addLocalFile(audioFile,
audioFile.right(audioFile.size() -
audioFile.lastIndexOf("/") - 1));
}
//close the archive so that everything is done
tar.close();
QSettings settings;
settings.setValue("lastSaveFile", file);
settings.sync();
qDebug() << settings.value("lastSaveFile");
return true;
}
return false;
}
bool ServiceItemModel::load(QUrl file) {
qDebug() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
qDebug() << "Loading...";
qDebug() << "File path is: " << file.toString();
qDebug() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
QString fileUrl = file.toString().right(file.toString().size() - 7);
KTar tar(fileUrl);
if (tar.open(QIODevice::ReadOnly)){
qDebug() << tar.isOpen();
const KArchiveDirectory *dir = tar.directory();
const KArchiveEntry *e = dir->entry("servicelist.json");
if (!e) {
qDebug() << "File not found!";
}
const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
QByteArray arr(f->data());
QJsonDocument jsonText = QJsonDocument::fromJson(arr);
qDebug() << jsonText; // the file contents
QJsonArray array = jsonText.array();
QVariantList serviceList = array.toVariantList();
qDebug() << serviceList;
// now lets remove all items from current list and add loaded ones
clearAll();
for (int i = 0; i < serviceList.length(); i++) {
// int id = serviceList
qDebug() << "*********************************";
qDebug() << serviceList[i].toMap();
qDebug() << "*********************************";
QMap item = serviceList[i].toMap();
QString backgroundString = item.value("background").toString();
QFileInfo backgroundFile = backgroundString.right(backgroundString.size() - 7);
QString audioString = item.value("audio").toString();
QFileInfo audioFile = audioString.right(audioString.size() - 7);
qDebug() << "POOPPOPOPOPOPOPOPOPOPOPOPOPO";
qDebug() << backgroundFile;
qDebug() << backgroundFile.exists();
qDebug() << audioFile;
qDebug() << audioFile.exists();
qDebug() << "POOPPOPOPOPOPOPOPOPOPOPOPOPO";
QString realBackground;
QString realAudio;
QFileInfo serviceFile = file.toString().right(file.toString().size() - 7);
QString serviceName = serviceFile.baseName();
QDir localDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
localDir.mkdir(serviceName);
QDir serviceDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)
+ "/" + serviceName;
qDebug() << serviceDir.path();
realBackground = backgroundString;
realAudio = audioString;
// If the background file is on disk use that, else use the one in archive
if (!backgroundFile.exists() && backgroundString.length() > 0) {
const KArchiveEntry *e = dir->entry(backgroundFile.fileName());
if (!e) {
qDebug() << "Background File not found!";
continue;
}
const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
if (!f->copyTo(serviceDir.path()))
qDebug() << "FILE COULDN'T BE CREATED!";
QFileInfo bgFile = serviceDir.path() + "/" + backgroundFile.fileName();
qDebug() << bgFile.filePath();
realBackground = bgFile.filePath();
}
// If the audio file is on disk use that, else use the one in archive
if (!audioFile.exists() && audioString.length() > 0) {
const KArchiveEntry *e = dir->entry(audioFile.fileName());
if (!e) {
qDebug() << "Audio File not found!";
continue;
}
const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
if (!f->copyTo(serviceDir.path()))
qDebug() << "FILE COULDN'T BE CREATED!";
QFileInfo audFile = serviceDir.path() + "/" + audioFile.fileName();
qDebug() << audFile.filePath();
realAudio = audFile.filePath();
}
insertItem(i, item.value("name").toString(), item.value("type").toString(),
realBackground,
item.value("backgroundType").toString(),
item.value("text").toStringList(), realAudio,
item.value("font").toString(), item.value("fontSize").toInt());
}
return true;
}
return false;
}
void ServiceItemModel::clearAll() {
for (int i = m_items.size(); i >= 0; i--) {
removeItem(i);
}
}
bool ServiceItemModel::loadLastSaved() {
QSettings settings;
return load(settings.value("lastSaveFile").toUrl());
}

103
src/cpp/serviceitemmodel.h Normal file
View file

@ -0,0 +1,103 @@
#ifndef SERVICEITEMMODEL_H
#define SERVICEITEMMODEL_H
#include "serviceitem.h"
#include <QAbstractListModel>
#include <qabstractitemmodel.h>
#include <qnamespace.h>
#include <qobjectdefs.h>
#include <qsize.h>
class ServiceItemModel : public QAbstractListModel {
Q_OBJECT
public:
explicit ServiceItemModel(QObject *parent = nullptr);
enum Roles {
NameRole = Qt::UserRole,
TypeRole,
BackgroundRole,
BackgroundTypeRole,
TextRole,
AudioRole,
FontRole,
FontSizeRole,
ActiveRole,
SelectedRole
};
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
// int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index,
int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
// Q_INVOKABLE int index(int row, int column,
// const QModelIndex &parent = QModelIndex()) const override;
// Q_INVOKABLE QModelIndex parent(const QModelIndex &index) const override;
// Editable:
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
// Helper methods
void addItem(ServiceItem *item);
void insertItem(const int &index, ServiceItem *item);
Q_INVOKABLE void addItem(const QString &name, const QString &type);
// Q_INVOKABLE void addItem(const QString &name, const QString &type,
// const QString &background);
Q_INVOKABLE void addItem(const QString &name, const QString &type,
const QString &background,
const QString &backgroundType);
Q_INVOKABLE void addItem(const QString &name, const QString &type,
const QString &background,
const QString &backgroundType,
const QStringList &text);
Q_INVOKABLE void addItem(const QString &name, const QString &type,
const QString &background,
const QString &backgroundType,
const QStringList &text, const QString &audio);
Q_INVOKABLE void addItem(const QString &name, const QString &type,
const QString &background,
const QString &backgroundType,
const QStringList &text, const QString &audio,
const QString &font, const int &fontSize);
Q_INVOKABLE void insertItem(const int &index, const QString &name,
const QString &type);
Q_INVOKABLE void insertItem(const int &index, const QString &name,
const QString &type, const QString &background,
const QString &backgroundType);
Q_INVOKABLE void insertItem(const int &index, const QString &name,
const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text);
Q_INVOKABLE void insertItem(const int &index, const QString &name,
const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
const QString &audio);
Q_INVOKABLE void insertItem(const int &index, const QString &name,
const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
const QString &audio, const QString &font, const int &fontSize);
Q_INVOKABLE void removeItem(int index);
Q_INVOKABLE bool moveRows(int sourceIndex, int destIndex, int count);
Q_INVOKABLE bool moveDown(int index);
Q_INVOKABLE bool moveUp(int index);
Q_INVOKABLE bool select(int id);
Q_INVOKABLE bool activate(int id);
Q_INVOKABLE bool deactivate(int id);
Q_INVOKABLE QVariantMap getItem(int index) const;
Q_INVOKABLE QVariantList getItems();
Q_INVOKABLE void clearAll();
Q_INVOKABLE bool save(QUrl file);
Q_INVOKABLE bool load(QUrl file);
Q_INVOKABLE bool loadLastSaved();
private:
QList<ServiceItem *> m_items;
};
#endif // SERVICEITEMMODEL_H

343
src/cpp/slide.cpp Normal file
View file

@ -0,0 +1,343 @@
#include "slide.h"
#include "serviceitemmodel.h"
#include <podofo/podofo.h>
#include <QDebug>
using namespace PoDoFo;
Slide::Slide(QObject *parent)
: QObject{parent}
{
qDebug() << "Initializing slide";
}
Slide::Slide(const QString &text, const QString &audio, const QString &imageBackground,
const QString &videoBackground, const QString &horizontalTextAlignment,
const QString &verticalTextAlignment, const QString &font,
const int &fontSize, const int &imageCount, const int &pdfIndex,
const bool &isPlaying, const QString &type, QObject *parent)
: QObject(parent),m_text(text),m_audio(audio),m_imageBackground(imageBackground),
m_videoBackground(videoBackground),m_verticalTextAlignment(verticalTextAlignment),
m_horizontalTextAlignment(horizontalTextAlignment),m_font(font),
m_fontSize(fontSize),m_imageCount(imageCount),m_pdfIndex(pdfIndex),
m_isPlaying(isPlaying),m_type(type)
{
qDebug() << "Initializing slide with defaults";
}
QString Slide::text() const {
return m_text;
}
QString Slide::type() const {
return m_type;
}
QVariantMap Slide::serviceItem() const {
return m_serviceItem;
}
QString Slide::audio() const {
return m_audio;
}
QString Slide::imageBackground() const
{
return m_imageBackground;
}
QString Slide::videoBackground() const
{
return m_videoBackground;
}
QString Slide::horizontalTextAlignment() const
{
return m_horizontalTextAlignment;
}
QString Slide::verticalTextAlignment() const
{
return m_verticalTextAlignment;
}
QString Slide::font() const
{
return m_font;
}
int Slide::fontSize() const
{
return m_fontSize;
}
int Slide::imageCount() const
{
return m_imageCount;
}
int Slide::pdfIndex() const
{
return m_pdfIndex;
}
bool Slide::isPlaying() const
{
return m_isPlaying;
}
void Slide::setText(QString text)
{
if (m_text == text)
return;
qDebug() << "####changing text to: " << text;
m_text = text;
emit textChanged(m_text);
}
void Slide::setType(QString type)
{
if (m_type == type)
return;
qDebug() << "####changing type to: " << type;
m_type = type;
emit typeChanged(m_type);
}
void Slide::setServiceItem(QVariantMap serviceItem)
{
if (m_serviceItem == serviceItem)
return;
qDebug() << "####changing serviceItem to: " << serviceItem;
m_serviceItem = serviceItem;
emit serviceItemChanged(m_serviceItem);
}
void Slide::setAudio(QString audio)
{
if (m_audio == audio)
return;
qDebug() << "####changing audio to: " << audio;
m_audio = audio;
emit audioChanged(m_audio);
}
void Slide::setImageBackground(QString imageBackground)
{
if (m_imageBackground == imageBackground)
return;
qDebug() << "####changing image background to: " << imageBackground;
m_imageBackground = imageBackground;
emit imageBackgroundChanged(m_imageBackground);
}
void Slide::setVideoBackground(QString videoBackground)
{
if (m_videoBackground == videoBackground)
return;
qDebug() << "####changing video background to: " << videoBackground;
m_videoBackground = videoBackground;
emit videoBackgroundChanged(m_videoBackground);
}
void Slide::setHorizontalTextAlignment(QString horizontalTextAlignment)
{
if (m_horizontalTextAlignment == horizontalTextAlignment)
return;
m_horizontalTextAlignment = horizontalTextAlignment;
emit horizontalTextAlignmentChanged(m_horizontalTextAlignment);
}
void Slide::setVerticalTextAlignment(QString verticalTextAlignment)
{
if (m_verticalTextAlignment == verticalTextAlignment)
return;
m_verticalTextAlignment = verticalTextAlignment;
emit verticalTextAlignmentChanged(m_verticalTextAlignment);
}
void Slide::setFont(QString font)
{
if (m_font == font)
return;
m_font = font;
emit fontChanged(m_font);
}
void Slide::setFontSize(int fontSize)
{
if (m_fontSize == fontSize)
return;
m_fontSize = fontSize;
emit fontSizeChanged(m_fontSize);
}
void Slide::setImageCount(int imageCount)
{
if (m_imageCount == imageCount)
return;
qDebug() << "####changing imageCount to: " << imageCount;
m_imageCount = imageCount;
emit imageCountChanged(m_imageCount);
}
void Slide::setPdfIndex(int pdfIndex)
{
if (m_pdfIndex == pdfIndex)
return;
qDebug() << "####changing pdfIndex to: " << pdfIndex;
m_pdfIndex = pdfIndex;
emit pdfIndexChanged(m_pdfIndex);
}
void Slide::changeSlide(QVariantMap item)
{
setServiceItem(item);
setType(m_serviceItem.value("type").toString());
qDebug() << "#$% SLIDE TYPE: " << type() << " %$#";
// First let's clear the text and then set
// the size and index of a basic slide
// then we'll build the rest
setText("");
m_slideSize = 1;
m_slideIndex = 1;
setPdfIndex(0);
qDebug() << serviceItem().value("backgroundType").toString();
if (serviceItem().value("backgroundType") == "image") {
setImageBackground(m_serviceItem.value("background").toString());
setVideoBackground("");
} else {
setVideoBackground(m_serviceItem.value("background").toString());
setImageBackground("");
}
setFont(m_serviceItem.value("font").toString());
setFontSize(m_serviceItem.value("fontSize").toInt());
setAudio("");
if (type() == "presentation") {
qDebug() << "#$#$#$#$ THIS PDF $#$#$#$#";
int pageCount;
QString str = imageBackground().remove(0,6);
qDebug() << str;
std::string file = str.toStdString();
// qDebug() << file;
const char * doc = file.c_str();
qDebug() << doc;
try {
PdfMemDocument pdf = PdfMemDocument(doc);
pageCount = pdf.GetPageCount();
} catch ( const PdfError & eCode ) {
eCode.PrintErrorMsg();
eCode.GetError();
return;
}
setImageCount(pageCount);
qDebug() << m_imageCount;
m_slideSize = m_imageCount;
}
QStringList text = m_serviceItem.value("text").toStringList();
if (type() == "song") {
qDebug() << "TEXT LENGTH: " << text.length();
m_slideSize = text.length();
m_slideIndex = 1;
setText(text[0]);
setAudio(serviceItem().value("audio").toString());
}
qDebug() << "MAP: " << m_serviceItem.value("text");
}
bool Slide::next(QVariantMap nextItem)
{
qDebug() << "Starting to go to next item.";
qDebug() << "Slide Index: " << m_slideIndex << " Slide Size: " << m_slideSize;
QStringList text = m_serviceItem.value("text").toStringList();
if (m_slideIndex == m_slideSize) {
// changeSlide(nextItem);
return true;
}
qDebug() << m_type;
// since the string list is 0 indexed m_slideIndex actually
// maps to the next item.
if (m_type == "song") {
int nextTextIndex = m_slideIndex;
qDebug() << nextTextIndex;
qDebug() << text[nextTextIndex];
setText(text[nextTextIndex]);
m_slideIndex++;
}
if (m_type == "presentation") {
qDebug() << "prev slide index: " << m_pdfIndex;
setPdfIndex(m_pdfIndex + 1);
qDebug() << "new slide index: " << m_pdfIndex;
m_slideIndex++;
}
return false;
}
bool Slide::previous(QVariantMap prevItem)
{
qDebug() << "Starting to go to previous item.";
qDebug() << "Slide Index: " << m_slideIndex << " Slide Size: " << m_slideSize;
QStringList text = m_serviceItem.value("text").toStringList();
if (m_slideIndex == 1) {
// changeSlide(prevItem);
return true;
}
// since the string list is 0 indexed m_slideIndex actually
// maps to the next item. So the prev text is minus 2
if (m_type == "song") {
int prevTextIndex = m_slideIndex - 2;
qDebug() << prevTextIndex;
qDebug() << text[prevTextIndex];
setText(text[prevTextIndex]);
m_slideIndex--;
}
if (m_type == "presentation") {
qDebug() << "prev slide index: " << m_pdfIndex;
setPdfIndex(m_pdfIndex - 1);
qDebug() << "new slide index: " << m_pdfIndex;
m_slideIndex--;
}
return false;
}
void Slide::play()
{
m_isPlaying = true;
emit isPlayingChanged(m_isPlaying);
}
void Slide::pause()
{
m_isPlaying = false;
emit isPlayingChanged(m_isPlaying);
}
void Slide::playPause()
{
m_isPlaying = !m_isPlaying;
emit isPlayingChanged(m_isPlaying);
}

109
src/cpp/slide.h Normal file
View file

@ -0,0 +1,109 @@
#ifndef SLIDE_H
#define SLIDE_H
#include <qobjectdefs.h>
#include <qqml.h>
#include <QObject>
#include <qobject.h>
class Slide : public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(QString type READ type WRITE setType NOTIFY typeChanged)
Q_PROPERTY(QVariantMap serviceItem READ serviceItem WRITE setServiceItem
NOTIFY serviceItemChanged)
Q_PROPERTY(QString audio READ audio WRITE setAudio NOTIFY audioChanged)
Q_PROPERTY(QString imageBackground READ imageBackground WRITE setImageBackground
NOTIFY imageBackgroundChanged)
Q_PROPERTY(QString videoBackground READ videoBackground WRITE setVideoBackground
NOTIFY videoBackgroundChanged)
Q_PROPERTY(QString horizontalTextAlignment READ horizontalTextAlignment
WRITE setHorizontalTextAlignment NOTIFY horizontalTextAlignmentChanged)
Q_PROPERTY(QString verticalTextAlignment READ verticalTextAlignment
WRITE setVerticalTextAlignment NOTIFY verticalTextAlignmentChanged)
Q_PROPERTY(QString font READ font WRITE setFont NOTIFY fontChanged)
Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
Q_PROPERTY(int imageCount READ imageCount WRITE setImageCount NOTIFY imageCountChanged)
Q_PROPERTY(int pdfIndex READ pdfIndex WRITE setPdfIndex NOTIFY pdfIndexChanged)
Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY isPlayingChanged)
// QML_ELEMENT
public:
explicit Slide(QObject *parent = nullptr);
Slide(const QString &text, const QString &audio, const QString &imageBackground, const QString &videoBackground,
const QString &horizontalTextAlignment, const QString &verticalTextAlignment,
const QString &font, const int &fontSize, const int &imageCount,
const int &pdfIndex, const bool &isPlaying, const QString &type,
QObject * parent = nullptr);
QString text() const;
QString type() const;
QVariantMap serviceItem() const;
QString audio() const;
QString imageBackground() const;
QString videoBackground() const;
QString horizontalTextAlignment() const;
QString verticalTextAlignment() const;
QString font() const;
int fontSize() const;
int imageCount() const;
int pdfIndex() const;
bool isPlaying() const;
Q_INVOKABLE void setText(QString text);
Q_INVOKABLE void setType(QString type);
Q_INVOKABLE void setServiceItem(QVariantMap serviceItem);
Q_INVOKABLE void setAudio(QString audio);
Q_INVOKABLE void setImageBackground(QString imageBackground);
Q_INVOKABLE void setVideoBackground(QString videoBackground);
Q_INVOKABLE void setHorizontalTextAlignment(QString horizontalTextAlignment);
Q_INVOKABLE void setVerticalTextAlignment(QString verticalTextAlignment);
Q_INVOKABLE void setFont(QString font);
Q_INVOKABLE void setFontSize(int fontSize);
Q_INVOKABLE void setImageCount(int imageCount);
Q_INVOKABLE void setPdfIndex(int pdfIndex);
Q_INVOKABLE void changeSlide(QVariantMap item);
Q_INVOKABLE void play();
Q_INVOKABLE void pause();
Q_INVOKABLE void playPause();
Q_INVOKABLE bool next(QVariantMap nextItem);
Q_INVOKABLE bool previous(QVariantMap prevItem);
signals:
Q_INVOKABLE void textChanged(QString text);
Q_INVOKABLE void typeChanged(QString type);
Q_INVOKABLE void serviceItemChanged(QVariantMap serviceItem);
Q_INVOKABLE void audioChanged(QString audio);
Q_INVOKABLE void imageBackgroundChanged(QString imageBackground);
Q_INVOKABLE void videoBackgroundChanged(QString videoBackground);
Q_INVOKABLE void horizontalTextAlignmentChanged(QString horizontalTextAlignment);
Q_INVOKABLE void verticalTextAlignmentChanged(QString verticalTextAlignment);
Q_INVOKABLE void fontChanged(QString font);
Q_INVOKABLE void fontSizeChanged(int fontSize);
Q_INVOKABLE void imageCountChanged(int imageCount);
Q_INVOKABLE void pdfIndexChanged(int pdfIndex);
Q_INVOKABLE void isPlayingChanged(bool isPlaying);
private:
int m_id;
QString m_text;
QString m_type;
QVariantMap m_serviceItem;
QString m_audio;
QString m_imageBackground;
QString m_videoBackground;
QString m_horizontalTextAlignment;
QString m_verticalTextAlignment;
QString m_font;
int m_fontSize;
int m_imageCount;
int m_pdfIndex;
bool m_isPlaying;
int m_slideIndex;
int m_slideSize;
};
#endif //SLIDE_H

711
src/cpp/songsqlmodel.cpp Normal file
View file

@ -0,0 +1,711 @@
#include "songsqlmodel.h"
#include <QDateTime>
#include <QDebug>
#include <QSqlError>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QSql>
#include <QSqlDatabase>
#include <qabstractitemmodel.h>
#include <qdebug.h>
#include <qglobal.h>
#include <qobjectdefs.h>
#include <qregexp.h>
#include <qsqlquery.h>
#include <qsqlrecord.h>
static const char *songsTableName = "songs";
static void createTable()
{
if(QSqlDatabase::database().tables().contains(songsTableName)) {
return;
}
QSqlQuery query;
if (!query.exec("CREATE TABLE IF NOT EXISTS 'songs' ("
" 'id' INTEGER NOT NULL,"
" 'title' TEXT NOT NULL,"
" 'lyrics' TEXT,"
" 'author' TEXT,"
" 'ccli' TEXT,"
" 'audio' TEXT,"
" 'vorder' TEXT,"
" 'background' TEXT,"
" 'backgroundType' TEXT,"
" 'horizontalTextAlignment' TEXT,"
" 'verticalTextAlignment' TEXT,"
" 'font' TEXT,"
" 'fontSize' INTEGER,"
" PRIMARY KEY(id))")) {
qFatal("Failed to query database: %s",
qPrintable(query.lastError().text()));
}
// qDebug() << query.lastQuery();
// qDebug() << "inserting into songs";
query.exec(
"INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, "
"background, backgroundType, horizontalTextAlignment, verticalTextAlignment, font, fontSize) VALUES ('10,000 Reasons', '10,000 reasons "
"for my heart to sing', 'Matt Redman', '13470183', '', '', '', '', 'center', 'center', '', '')");
// qDebug() << query.lastQuery();
query.exec("INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, "
"background, backgroundType, horizontalTextAlignment, verticalTextAlignment, font, fontSize) VALUES ('River', 'Im going down to "
"the river', 'Jordan Feliz', '13470183', '', '', '', '', 'center', 'center', '', '')");
query.exec(
"INSERT INTO songs (title, lyrics, author, ccli, audio, vorder, "
"background, backgroundType, horizontalTextAlignment, verticalTextAlignment, font, fontSize) VALUES ('Marvelous Light', 'Into marvelous "
"light Im running', 'Chris Tomlin', '13470183', '', '', '', '', 'center', 'center', '', '')");
// qDebug() << query.lastQuery();
query.exec("select * from songs");
// qDebug() << query.lastQuery();
}
SongSqlModel::SongSqlModel(QObject *parent)
: QSqlTableModel(parent)
{
// qDebug() << "creating table";
createTable();
setTable(songsTableName);
setEditStrategy(QSqlTableModel::OnManualSubmit);
// make sure to call select else the model won't fill
select();
}
QVariant SongSqlModel::data(const QModelIndex &index, int role) const {
if (role < Qt::UserRole) {
// qDebug() << role;
return QSqlTableModel::data(index, role);
}
// qDebug() << role;
const QSqlRecord sqlRecord = record(index.row());
return sqlRecord.value(role - Qt::UserRole);
}
QHash<int, QByteArray> SongSqlModel::roleNames() const
{
QHash<int, QByteArray> names;
names[Qt::UserRole] = "id";
names[Qt::UserRole + 1] = "title";
names[Qt::UserRole + 2] = "lyrics";
names[Qt::UserRole + 3] = "author";
names[Qt::UserRole + 4] = "ccli";
names[Qt::UserRole + 5] = "audio";
names[Qt::UserRole + 6] = "vorder";
names[Qt::UserRole + 7] = "background";
names[Qt::UserRole + 8] = "backgroundType";
names[Qt::UserRole + 9] = "horizontalTextAlignment";
names[Qt::UserRole + 10] = "verticalTextAlignment";
names[Qt::UserRole + 11] = "font";
names[Qt::UserRole + 12] = "fontSize";
return names;
}
void SongSqlModel::newSong() {
qDebug() << "starting to add new song";
int rows = rowCount();
qDebug() << rows;
QSqlRecord recorddata = record();
recorddata.setValue("title", "new song");
qDebug() << recorddata;
if (insertRecord(rows, recorddata)) {
submitAll();
} else {
qDebug() << lastError();
};
}
void SongSqlModel::deleteSong(const int &row) {
QSqlRecord recordData = record(row);
if (recordData.isEmpty())
return;
removeRow(row);
submitAll();
}
QVariantMap SongSqlModel::getSong(const int &row) {
// this whole function returns all data in the song
// regardless of it's length. When new things are added
// it will still work without refactoring.
const QModelIndex &parent = QModelIndex();
QVariantMap data;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
const QModelIndex idx = this->index(id, 0, parent);
qDebug() << "%%%%%%%%%";
qDebug() << row;
qDebug() << idx;
qDebug() << "%%%%%%%%%";
if( !idx.isValid() )
return data;
const QHash<int,QByteArray> rn = roleNames();
// qDebug() << rn;
QHashIterator<int,QByteArray> it(rn);
while (it.hasNext()) {
it.next();
qDebug() << it.key() << ":" << it.value();
data[it.value()] = idx.data(it.key());
}
return data;
}
QStringList SongSqlModel::getLyricList(const int &row) {
QSqlQuery query("select id from songs");
QList<int> ids;
qDebug() << row;
while (query.next()) {
ids.append(query.value(0).toInt());
qDebug() << ids;
}
int id = ids.indexOf(row,0);
qDebug() << "@@@@@@@@@@@@@@";
qDebug() << id;
qDebug() << "@@@@@@@@@@@@@@";
QSqlRecord recordData = record(id);
if (recordData.isEmpty()) {
qDebug() << "this is not a song";
QStringList empty;
return empty;
}
QStringList rawLyrics = recordData.value("lyrics").toString().split("\n");
qDebug() << "HERE ARE RAW LYRICS: " << rawLyrics;
QStringList lyrics;
QStringList vorder = recordData.value("vorder").toString().split(" ");
qDebug() << vorder;
QStringList keywords = {"Verse 1", "Verse 2", "Verse 3", "Verse 4",
"Verse 5", "Verse 6", "Verse 7", "Verse 8",
"Chorus 1", "Chorus 2", "Chorus 3", "Chorus 4",
"Bridge 1", "Bridge 2", "Bridge 3", "Bridge 4",
"Intro 1", "Intro 2", "Ending 1", "Ending 2",
"Other 1", "Other 2", "Other 3", "Other 4"};
bool firstItem = true;
QString verse;
QString vtitle;
QString line;
QMultiMap<QString, QString> verses;
//TODO make sure to split empty line in verse into two slides
// This first function pulls out each verse into our verses map
// foreach (line, rawLyrics) {
for (int i = 0; i < rawLyrics.length(); ++i) {
qDebug() << "##########################";
qDebug() << rawLyrics[i];
qDebug() << rawLyrics.length();
qDebug() << i;
qDebug() << "##########################";
if (firstItem) {
if (keywords.contains(rawLyrics[i])) {
qDebug() << "!!!!THIS IS FIRST LINE!!!!!";
// qDebug() << rawLyrics[i];
firstItem = false;
vtitle = rawLyrics[i];
continue;
}
} else if (keywords.contains(rawLyrics[i])) {
qDebug() << "!!!!THIS IS A VTITLE!!!!!";
// qDebug() << verse;
// qDebug() << rawLyrics[i];
if (verse.contains("\n\n")) {
verse = verse.trimmed();
qDebug() << "THIS IS A EMPTY SLIDE!" << verse;
QStringList multiverses = verse.split("\n\n");
foreach (verse, multiverses) {
verses.insert(vtitle, verse);
// qDebug() << verse;
}
verse.clear();
multiverses.clear();
vtitle = rawLyrics[i];
continue;
}
verses.insert(vtitle, verse);
verse.clear();
vtitle = rawLyrics[i];
continue;
} else if (i + 1 == rawLyrics.length()) {
qDebug() << "!!!!LAST LINE!!!!!";
verse.append(rawLyrics[i].trimmed() + "\n");
if (verse.contains("\n\n")) {
verse = verse.trimmed();
qDebug() << "THIS IS A EMPTY SLIDE!" << verse;
QStringList multiverses = verse.split("\n\n");
foreach (verse, multiverses) {
verses.insert(vtitle, verse);
// qDebug() << verse;
}
break;
}
verses.insert(vtitle, verse);
qDebug() << "&&&&&&&&&&&&";
qDebug() << "This is final line";
qDebug() << "and has been inserted";
qDebug() << verses.values(vtitle);
qDebug() << "&&&&&&&&&&&&";
break;
}
qDebug() << "THIS RAWLYRICS[I]";
qDebug() << rawLyrics[i];
qDebug() << "THIS VTITLE";
qDebug() << vtitle;
verse.append(rawLyrics[i].trimmed() + "\n");
qDebug() << verse;
qDebug() << "APPENDED VERSE";
}
// qDebug() << verses;
// let's check to see if there is a verse order, if not return the list given
if (vorder.first().isEmpty()) {
qDebug() << "NO VORDER";
foreach (verse, verses) {
qDebug() << verse;
lyrics.append(verse);
}
qDebug() << lyrics;
return lyrics;
}
// this function appends the verse that matches the verse order from the map
// first we run through every line and check to see if the line matches
// an item in vorder, then we append lyrics from the verse that matches
// the verse map we created earlier. It's a multi map so we need to append
// them in reverse as they are added in last in first out order.
foreach (const QString &vstr, vorder) {
foreach (line, rawLyrics) {
if (line.startsWith(vstr.at(0)) && line.endsWith(vstr.at(1))) {
qDebug() << "**********************";
qDebug() << vstr;
qDebug() << line;
qDebug() << "**********************";
QList<QString> values = verses.values(line);
for (int i = values.size(); i > 0;)
lyrics.append(values.at(--i));
}
}
}
// qDebug() << lyrics;
return lyrics;
}
int SongSqlModel::id() const {
return m_id;
}
QString SongSqlModel::title() const {
return m_title;
}
void SongSqlModel::setTitle(const QString &title) {
if (title == m_title)
return;
m_title = title;
select();
emit titleChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateTitle(const int &row, const QString &title) {
qDebug() << "Row is " << row;
// QSqlQuery query("select id from songs");
// QList<int> ids;
// while (query.next()) {
// ids.append(query.value(0).toInt());
// // qDebug() << ids;
// }
// int id = ids.indexOf(row,0);
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("title", title);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit titleChanged();
}
QString SongSqlModel::author() const {
return m_author;
}
void SongSqlModel::setAuthor(const QString &author) {
if (author == m_author)
return;
m_author = author;
select();
emit authorChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateAuthor(const int &row, const QString &author) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
rowdata.setValue("author", author);
setRecord(id, rowdata);
submitAll();
emit authorChanged();
}
QString SongSqlModel::lyrics() const {
return m_lyrics;
}
void SongSqlModel::setLyrics(const QString &lyrics) {
if (lyrics == m_lyrics)
return;
m_lyrics = lyrics;
select();
emit lyricsChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateLyrics(const int &row, const QString &lyrics) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << lyrics;
rowdata.setValue("lyrics", lyrics);
qDebug() << rowdata.value("lyrics");
setRecord(id, rowdata);
submitAll();
emit lyricsChanged();
}
QString SongSqlModel::ccli() const {
return m_ccli;
}
void SongSqlModel::setCcli(const QString &ccli) {
if (ccli == m_ccli)
return;
m_ccli = ccli;
select();
emit ccliChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateCcli(const int &row, const QString &ccli) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
rowdata.setValue("ccli", ccli);
setRecord(id, rowdata);
submitAll();
emit ccliChanged();
}
QString SongSqlModel::audio() const {
return m_audio;
}
void SongSqlModel::setAudio(const QString &audio) {
if (audio == m_audio)
return;
m_audio = audio;
select();
emit audioChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateAudio(const int &row, const QString &audio) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
rowdata.setValue("audio", audio);
setRecord(id, rowdata);
submitAll();
emit audioChanged();
}
QString SongSqlModel::vorder() const { return m_vorder; }
void SongSqlModel::setVerseOrder(const QString &vorder) {
if (vorder == m_vorder)
return;
m_vorder = vorder;
select();
emit vorderChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateVerseOrder(const int &row, const QString &vorder) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
rowdata.setValue("vorder", vorder);
setRecord(id, rowdata);
submitAll();
emit vorderChanged();
}
QString SongSqlModel::background() const { return m_background; }
void SongSqlModel::setBackground(const QString &background) {
if (background == m_background)
return;
m_background = background;
select();
emit backgroundChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateBackground(const int &row, const QString &background) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
rowdata.setValue("background", background);
setRecord(id, rowdata);
submitAll();
emit backgroundChanged();
}
QString SongSqlModel::backgroundType() const { return m_backgroundType; }
void SongSqlModel::setBackgroundType(const QString &backgroundType) {
if (backgroundType == m_backgroundType)
return;
m_backgroundType = backgroundType;
select();
emit backgroundTypeChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateBackgroundType(const int &row, const QString &backgroundType) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
rowdata.setValue("backgroundType", backgroundType);
setRecord(id, rowdata);
submitAll();
emit backgroundTypeChanged();
}
QString SongSqlModel::horizontalTextAlignment() const {
return m_horizontalTextAlignment;
}
void SongSqlModel::setHorizontalTextAlignment(const QString &horizontalTextAlignment) {
if (horizontalTextAlignment == m_horizontalTextAlignment)
return;
m_horizontalTextAlignment = horizontalTextAlignment;
select();
emit horizontalTextAlignmentChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateHorizontalTextAlignment(const int &row, const QString &horizontalTextAlignment) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("horizontalTextAlignment", horizontalTextAlignment);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit horizontalTextAlignmentChanged();
}
QString SongSqlModel::verticalTextAlignment() const {
return m_verticalTextAlignment;
}
void SongSqlModel::setVerticalTextAlignment(const QString &verticalTextAlignment) {
if (verticalTextAlignment == m_verticalTextAlignment)
return;
m_verticalTextAlignment = verticalTextAlignment;
select();
emit verticalTextAlignmentChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateVerticalTextAlignment(const int &row, const QString &verticalTextAlignment) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("verticalTextAlignment", verticalTextAlignment);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit verticalTextAlignmentChanged();
}
QString SongSqlModel::font() const {
return m_font;
}
void SongSqlModel::setFont(const QString &font) {
if (font == m_font)
return;
m_font = font;
select();
emit fontChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateFont(const int &row, const QString &font) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("font", font);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit fontChanged();
}
int SongSqlModel::fontSize() const {
return m_fontSize;
}
void SongSqlModel::setFontSize(const int &fontSize) {
if (fontSize == m_fontSize)
return;
m_fontSize = fontSize;
select();
emit fontSizeChanged();
}
// This function is for updating the lyrics from outside the delegate
void SongSqlModel::updateFontSize(const int &row, const int &fontSize) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from songs");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("fontSize", fontSize);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit fontSizeChanged();
}

110
src/cpp/songsqlmodel.h Normal file
View file

@ -0,0 +1,110 @@
#ifndef SONGSQLMODEL_H
#define SONGSQLMODEL_H
#include <QSqlTableModel>
#include <qobjectdefs.h>
#include <qqml.h>
#include <qvariant.h>
class SongSqlModel : public QSqlTableModel
{
Q_OBJECT
Q_PROPERTY(int id READ id)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QString lyrics READ lyrics WRITE setLyrics NOTIFY lyricsChanged)
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QString ccli READ ccli WRITE setCcli NOTIFY ccliChanged)
Q_PROPERTY(QString audio READ audio WRITE setAudio NOTIFY audioChanged)
Q_PROPERTY(QString vorder READ vorder WRITE setVerseOrder NOTIFY vorderChanged)
Q_PROPERTY(QString background READ background WRITE setBackground NOTIFY backgroundChanged)
Q_PROPERTY(QString backgroundType READ backgroundType WRITE setBackgroundType NOTIFY backgroundTypeChanged)
Q_PROPERTY(QString horizontalTextAlignment READ horizontalTextAlignment WRITE setHorizontalTextAlignment NOTIFY horizontalTextAlignmentChanged)
Q_PROPERTY(QString verticalTextAlignment READ verticalTextAlignment WRITE setVerticalTextAlignment NOTIFY verticalTextAlignmentChanged)
Q_PROPERTY(QString font READ font WRITE setFont NOTIFY fontChanged)
Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
QML_ELEMENT
public:
SongSqlModel(QObject *parent = 0);
int id() const;
QString title() const;
QString lyrics() const;
QString author() const;
QString ccli() const;
QString audio() const;
QString vorder() const;
QString background() const;
QString backgroundType() const;
QString horizontalTextAlignment() const;
QString verticalTextAlignment() const;
QString font() const;
int fontSize() const;
void setTitle(const QString &title);
void setLyrics(const QString &lyrics);
void setAuthor(const QString &author);
void setCcli(const QString &ccli);
void setAudio(const QString &audio);
void setVerseOrder(const QString &vorder);
void setBackground(const QString &background);
void setBackgroundType(const QString &backgroundType);
void setHorizontalTextAlignment(const QString &horizontalTextAlignment);
void setVerticalTextAlignment(const QString &verticalTextAlignment);
void setFont(const QString &font);
void setFontSize(const int &fontSize);
Q_INVOKABLE void updateTitle(const int &row, const QString &title);
Q_INVOKABLE void updateLyrics(const int &row, const QString &lyrics);
Q_INVOKABLE void updateAuthor(const int &row, const QString &author);
Q_INVOKABLE void updateCcli(const int &row, const QString &ccli);
Q_INVOKABLE void updateAudio(const int &row, const QString &audio);
Q_INVOKABLE void updateVerseOrder(const int &row, const QString &vorder);
Q_INVOKABLE void updateBackground(const int &row, const QString &background);
Q_INVOKABLE void updateBackgroundType(const int &row, const QString &backgroundType);
Q_INVOKABLE void updateHorizontalTextAlignment(const int &row,
const QString &horizontalTextAlignment);
Q_INVOKABLE void updateVerticalTextAlignment(const int &row,
const QString &horizontalTextAlignment);
Q_INVOKABLE void updateFont(const int &row, const QString &font);
Q_INVOKABLE void updateFontSize(const int &row, const int &fontSize);
Q_INVOKABLE void newSong();
Q_INVOKABLE void deleteSong(const int &row);
Q_INVOKABLE QVariantMap getSong(const int &row);
Q_INVOKABLE QStringList getLyricList(const int &row);
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
signals:
void titleChanged();
void lyricsChanged();
void authorChanged();
void ccliChanged();
void audioChanged();
void vorderChanged();
void backgroundChanged();
void backgroundTypeChanged();
void horizontalTextAlignmentChanged();
void verticalTextAlignmentChanged();
void fontChanged();
void fontSizeChanged();
private:
int m_id;
QString m_title;
QString m_lyrics;
QString m_author;
QString m_ccli;
QString m_audio;
QString m_vorder;
QString m_background;
QString m_backgroundType;
QString m_horizontalTextAlignment;
QString m_verticalTextAlignment;
QString m_font;
int m_fontSize;
};
#endif //SONGSQLMODEL_H

159
src/cpp/thumbnail.cpp Normal file
View file

@ -0,0 +1,159 @@
#include "thumbnail.h"
#include <QDebug>
Thumbnail::Thumbnail(QObject *parent)
: QObject{parent}
{
}
Thumbnail::Thumbnail(const QString &owner, const QString &type, QObject *parent)
: QObject(parent),m_owner(owner),m_type(type)
{
}
Thumbnail::Thumbnail(const QString &owner, const QString &type, const QString &background,
const QString &backgroundType, QObject *parent)
: QObject(parent),m_owner(owner),m_type(type),m_background(background),
m_backgroundType(backgroundType)
{
}
Thumbnail::Thumbnail(const QString &owner, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
QObject *parent)
: QObject(parent),m_owner(owner),m_type(type),m_background(background),
m_backgroundType(backgroundType),m_text(text)
{
}
Thumbnail::Thumbnail(const QString &owner, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
const QString &songVerse, QObject *parent)
: QObject(parent),m_owner(owner),m_type(type),m_background(background),
m_backgroundType(backgroundType),m_text(text),m_songVerse(songVerse)
{
}
Thumbnail::Thumbnail(const QString &owner, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
const QString &font, const int &fontSize, const QString &songVerse
QObject *parent)
: QObject(parent),m_owner(owner),m_type(type),m_background(background),
m_backgroundType(backgroundType),m_text(text),m_font(font),m_fontSize(fontSize),
m_songVerse(songVerse)
{
}
QString Thumbnail::owner() const {
return m_owner;
}
QString Thumbnail::type() const {
return m_type;
}
QString Thumbnail::songVerse() const {
return m_songVerse;
}
QString Thumbnail::background() const
{
return m_background;
}
QString Thumbnail::backgroundType() const
{
return m_backgroundType;
}
QStringList Thumbnail::text() const
{
return m_text;
}
QString Thumbnail::font() const {
return m_font;
}
int Thumbnail::fontSize() const {
return m_fontSize;
}
void Thumbnail::setOwner(QString owner)
{
if (m_owner == owner)
return;
m_owner = owner;
emit ownerChanged(m_owner);
}
void Thumbnail::setType(QString type)
{
if (m_type == type)
return;
m_type = type;
emit typeChanged(m_type);
}
void Thumbnail::setSongVerse(QString songVerse)
{
if (m_songVerse == songVerse)
return;
m_songVerse = songVerse;
emit songVerseChanged(m_songVerse);
}
void Thumbnail::setBackground(QString background)
{
if (m_background == background)
return;
m_background = background;
emit backgroundChanged(m_background);
}
void Thumbnail::setBackgroundType(QString backgroundType)
{
if (m_backgroundType == backgroundType)
return;
m_backgroundType = backgroundType;
emit backgroundTypeChanged(m_backgroundType);
}
void Thumbnail::setText(QStringList text)
{
if (m_text == text)
return;
m_text = text;
emit textChanged(m_text);
}
void Thumbnail::setFont(QString font)
{
if (m_font == font)
return;
m_font = font;
emit fontChanged(m_font);
}
void Thumbnail::setFontSize(int fontSize)
{
if (m_fontSize == fontSize)
return;
m_fontSize = fontSize;
emit fontSizeChanged(m_fontSize);
}

74
src/cpp/thumbnail.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef THUMBNAIL_H
#define THUMBNAIL_H
#include <QObject>
#include <qobject.h>
class Thumbnail : public QObject
{
Q_OBJECT
Q_PROPERTY(QString owner READ owner WRITE setOwner NOTIFY ownerChanged)
Q_PROPERTY(QString type READ type WRITE setType NOTIFY typeChanged)
Q_PROPERTY(QString songVerse READ songVerse WRITE setSongVerse NOTIFY songVerseChanged)
Q_PROPERTY(QString background READ background WRITE setBackground NOTIFY backgroundChanged)
Q_PROPERTY(QString backgroundType READ backgroundType WRITE setBackgroundType NOTIFY backgroundTypeChanged)
Q_PROPERTY(QStringList text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(QString font READ font WRITE setFont NOTIFY fontChanged)
Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
public:
explicit Thumbnail(QObject *parent = nullptr);
Thumbnail(const QString &owner, const QString &type, QObject * parent = nullptr);
Thumbnail(const QString &owner, const QString &type, const QString &background,
const QString &backgroundType, QObject * parent = nullptr);
Thumbnail(const QString &owner, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
QObject * parent = nullptr);
Thumbnail(const QString &owner, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
const QString &songVerse, QObject * parent = nullptr);
Thumbnail(const QString &owner, const QString &type, const QString &background,
const QString &backgroundType, const QStringList &text,
const QString &songVerse, const QString &font,
const int &fontSize, QObject * parent = nullptr);
QString owner() const;
QString type() const;
QString songVerse() const;
QString background() const;
QString backgroundType() const;
QStringList text() const;
QString font() const;
int fontSize() const;
void setOwner(QString owner);
void setType(QString type);
void setSongVerse(QString songVerse);
void setBackground(QString background);
void setBackgroundType(QString backgroundType);
void setText(QStringList text);
void setFont(QString font);
void setFontSize(int fontSize);
signals:
void ownerChanged(QString owner);
void typeChanged(QString type);
void songVerseChanged(QString songVerse);
void backgroundChanged(QString background);
void backgroundTypeChanged(QString backgroundType);
void textChanged(QStringList text);
void fontChanged(QString font);
void fontSizeChanged(int fontSize);
private:
QString m_owner;
QString m_type;
QString m_songVerse;
QString m_background;
QString m_backgroundType;
QStringList m_text;
QString m_font;
int m_fontSize;
};
#endif // THUMBNAIL_H

316
src/cpp/videosqlmodel.cpp Normal file
View file

@ -0,0 +1,316 @@
#include "videosqlmodel.h"
#include <QDateTime>
#include <QDebug>
#include <QSqlError>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QSql>
#include <QSqlDatabase>
#include <QFileInfo>
#include <qabstractitemmodel.h>
#include <qdebug.h>
#include <qnamespace.h>
#include <qobject.h>
#include <qobjectdefs.h>
#include <qsqlrecord.h>
#include <qurl.h>
#include <qvariant.h>
static const char *videosTableName = "videos";
static void createVideoTable()
{
if(QSqlDatabase::database().tables().contains(videosTableName)) {
return;
}
QSqlQuery query;
if (!query.exec("CREATE TABLE IF NOT EXISTS 'videos' ("
" 'id' INTEGER NOT NULL,"
" 'title' TEXT NOT NULL,"
" 'filePath' TEXT NOT NULL,"
" 'startTime' REAL,"
" 'endTime' REAL,"
" 'loop' BOOLEAN NOT NULL DEFAULT 0,"
" PRIMARY KEY(id))")) {
qFatal("Failed to query database: %s",
qPrintable(query.lastError().text()));
}
qDebug() << query.lastQuery();
qDebug() << "inserting into videos";
query.exec("INSERT INTO videos (title, filePath) VALUES ('The Test', '/home/chris/nextcloud/tfc/openlp/videos/test.mp4')");
qDebug() << query.lastQuery();
query.exec("INSERT INTO videos (title, filePath) VALUES ('Sabbath', '/home/chris/nextcloud/tfc/openlp/videos/Sabbath.mp4')");
query.exec("select * from videos");
qDebug() << query.lastQuery();
}
VideoSqlModel::VideoSqlModel(QObject *parent) : QSqlTableModel(parent) {
qDebug() << "creating video table";
createVideoTable();
QSqlQuery query("PRAGMA table_info(videos)");
while (query.next()) {
QString title = query.value(1).toString();
qDebug() << title;
}
setTable(videosTableName);
setEditStrategy(QSqlTableModel::OnManualSubmit);
// make sure to call select else the model won't fill
select();
}
QVariant VideoSqlModel::data(const QModelIndex &index, int role) const {
if (role < Qt::UserRole) {
return QSqlTableModel::data(index, role);
}
// qDebug() << role;
const QSqlRecord sqlRecord = record(index.row());
return sqlRecord.value(role - Qt::UserRole);
}
QHash<int, QByteArray> VideoSqlModel::roleNames() const
{
QHash<int, QByteArray> names;
names[Qt::UserRole] = "id";
names[Qt::UserRole + 1] = "title";
names[Qt::UserRole + 2] = "filePath";
names[Qt::UserRole + 3] = "startTime";
names[Qt::UserRole + 4] = "endTime";
names[Qt::UserRole + 5] = "loop";
return names;
}
void VideoSqlModel::newVideo(const QUrl &filePath) {
qDebug() << "adding new video";
int rows = rowCount();
qDebug() << rows;
QSqlRecord recordData = record();
QFileInfo fileInfo = filePath.toString();
QString title = fileInfo.baseName();
recordData.setValue("title", title);
recordData.setValue("filePath", filePath);
recordData.setValue("loop", 0);
if (insertRecord(rows, recordData)) {
submitAll();
} else {
qDebug() << lastError();
};
}
void VideoSqlModel::deleteVideo(const int &row) {
QSqlRecord recordData = record(row);
if (recordData.isEmpty())
return;
removeRow(row);
submitAll();
}
int VideoSqlModel::id() const {
return m_id;
}
QString VideoSqlModel::title() const {
return m_title;
}
void VideoSqlModel::setTitle(const QString &title) {
if (title == m_title)
return;
m_title = title;
select();
emit titleChanged();
}
// This function is for updating the title from outside the delegate
void VideoSqlModel::updateTitle(const int &row, const QString &title) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from videos");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("title", title);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit titleChanged();
}
QUrl VideoSqlModel::filePath() const {
return m_filePath;
}
void VideoSqlModel::setFilePath(const QUrl &filePath) {
if (filePath == m_filePath)
return;
m_filePath = filePath;
select();
emit filePathChanged();
}
// This function is for updating the filepath from outside the delegate
void VideoSqlModel::updateFilePath(const int &row, const QUrl &filePath) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from videos");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("filePath", filePath);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit filePathChanged();
}
int VideoSqlModel::startTime() const {
return m_startTime;
}
void VideoSqlModel::setStartTime(const int &startTime) {
if (startTime == m_startTime)
return;
m_startTime = startTime;
select();
emit startTimeChanged();
}
// This function is for updating the title from outside the delegate
void VideoSqlModel::updateStartTime(const int &row, const int &startTime) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from videos");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("startTime", startTime);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit startTimeChanged();
}
int VideoSqlModel::endTime() const {
return m_endTime;
}
void VideoSqlModel::setEndTime(const int &endTime) {
if (endTime == m_endTime)
return;
m_endTime = endTime;
select();
emit endTimeChanged();
}
// This function is for updating the title from outside the delegate
void VideoSqlModel::updateEndTime(const int &row, const int &endTime) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from videos");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("endTime", endTime);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit endTimeChanged();
}
bool VideoSqlModel::loop() const {
return m_loop;
}
void VideoSqlModel::setLoop(const bool &loop) {
if (loop == m_loop)
return;
m_loop = loop;
select();
emit loopChanged();
}
// This function is for updating looping from outside the delegate
void VideoSqlModel::updateLoop(const int &row, const bool &loop) {
qDebug() << "Row is " << row;
QSqlQuery query("select id from videos");
QList<int> ids;
while (query.next()) {
ids.append(query.value(0).toInt());
// qDebug() << ids;
}
int id = ids.indexOf(row,0);
QSqlRecord rowdata = record(id);
qDebug() << rowdata;
rowdata.setValue("loop", loop);
setRecord(id, rowdata);
qDebug() << rowdata;
submitAll();
emit loopChanged();
}
QVariantMap VideoSqlModel::getVideo(const int &row) {
// qDebug() << "Row we are getting is " << row;
// QVariantList video;
// QSqlRecord rec = record(row);
// qDebug() << rec.value("title");
// video.append(rec.value("title"));
// video.append(rec.value("filePath"));
// return video;
QVariantMap data;
const QModelIndex idx = this->index(row,0);
// qDebug() << idx;
if( !idx.isValid() )
return data;
const QHash<int,QByteArray> rn = roleNames();
// qDebug() << rn;
QHashIterator<int,QByteArray> it(rn);
while (it.hasNext()) {
it.next();
qDebug() << it.key() << ":" << it.value();
data[it.value()] = idx.data(it.key());
}
return data;
}

67
src/cpp/videosqlmodel.h Normal file
View file

@ -0,0 +1,67 @@
#ifndef VIDEOSQLMODEL_H
#define VIDEOSQLMODEL_H
#include <QSqlTableModel>
#include <qobject.h>
#include <qobjectdefs.h>
#include <qqml.h>
#include <qurl.h>
#include <qvariant.h>
class VideoSqlModel : public QSqlTableModel
{
Q_OBJECT
Q_PROPERTY(int id READ id)
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QUrl filePath READ filePath WRITE setFilePath NOTIFY filePathChanged)
Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(int endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
Q_PROPERTY(bool loop READ loop WRITE setLoop NOTIFY loopChanged)
QML_ELEMENT
public:
VideoSqlModel(QObject *parent = 0);
int id() const;
QString title() const;
QUrl filePath() const;
int startTime() const;
int endTime() const;
bool loop() const;
void setTitle(const QString &title);
void setFilePath(const QUrl &filePath);
void setStartTime(const int &startTime);
void setEndTime(const int &endTime);
void setLoop(const bool &loop);
Q_INVOKABLE void updateTitle(const int &row, const QString &title);
Q_INVOKABLE void updateFilePath(const int &row, const QUrl &filePath);
Q_INVOKABLE void updateStartTime(const int &row, const int &startTime);
Q_INVOKABLE void updateEndTime(const int &row, const int &endTime);
Q_INVOKABLE void updateLoop(const int &row, const bool &loop);
Q_INVOKABLE void newVideo(const QUrl &filePath);
Q_INVOKABLE void deleteVideo(const int &row);
Q_INVOKABLE QVariantMap getVideo(const int &row);
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
signals:
void titleChanged();
void filePathChanged();
void loopChanged();
void startTimeChanged();
void endTimeChanged();
private:
int m_id;
QString m_title;
QUrl m_filePath;
int m_startTime;
int m_endTime;
bool m_loop;
};
#endif //VIDEOSQLMODEL_H