adding in a video model and editor
This commit is contained in:
parent
fab9f86b41
commit
c35c0f6550
22 changed files with 972 additions and 160 deletions
|
@ -3,8 +3,8 @@ add_executable(presenter)
|
|||
target_sources(presenter
|
||||
PRIVATE
|
||||
main.cpp resources.qrc
|
||||
songlistmodel.cpp songlistmodel.h
|
||||
songsqlmodel.cpp songsqlmodel.h
|
||||
videosqlmodel.cpp videosqlmodel.h
|
||||
mpv/mpvobject.h mpv/mpvobject.cpp
|
||||
mpv/qthelper.hpp mpv/mpvhelpers.h
|
||||
)
|
||||
|
|
11
src/main.cpp
11
src/main.cpp
|
@ -30,9 +30,9 @@
|
|||
#include <qsqlquery.h>
|
||||
#include <qstringliteral.h>
|
||||
|
||||
#include "songlistmodel.h"
|
||||
#include "mpv/mpvobject.h"
|
||||
#include "songsqlmodel.h"
|
||||
#include "videosqlmodel.h"
|
||||
|
||||
static void connectToDatabase() {
|
||||
// let's setup our sql database
|
||||
|
@ -44,6 +44,7 @@ static void connectToDatabase() {
|
|||
}
|
||||
|
||||
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()));
|
||||
|
@ -79,21 +80,21 @@ int main(int argc, char *argv[])
|
|||
#endif
|
||||
|
||||
QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("system-config-display")));
|
||||
|
||||
// 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 song model from sql
|
||||
//register our models
|
||||
qmlRegisterType<SongSqlModel>("org.presenter", 1, 0, "SongSqlModel");
|
||||
|
||||
SongListModel songListModel;
|
||||
qmlRegisterType<VideoSqlModel>("org.presenter", 1, 0, "VideoSqlModel");
|
||||
|
||||
connectToDatabase();
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
||||
engine.rootContext()->setContextProperty("_songListModel", &songListModel);
|
||||
engine.load(QUrl(QStringLiteral("qrc:qml/main.qml")));
|
||||
|
||||
// QQuickView *view = new QQuickView;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "mpvobject.h"
|
||||
|
||||
// std
|
||||
#include <qdir.h>
|
||||
#include <qvariant.h>
|
||||
#include <stdexcept>
|
||||
#include <clocale>
|
||||
|
||||
|
@ -19,6 +21,7 @@
|
|||
#include <QtX11Extras/QX11Info>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QStandardPaths>
|
||||
|
||||
// libmpv
|
||||
#include <mpv/client.h>
|
||||
|
@ -28,6 +31,7 @@
|
|||
#include "qthelper.hpp"
|
||||
|
||||
|
||||
const QDir writeDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
|
||||
//--- MpvRenderer
|
||||
void* MpvRenderer::get_proc_address(void *ctx, const char *name) {
|
||||
|
@ -261,11 +265,13 @@ void MpvObject::doUpdate()
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -522,6 +528,17 @@ void MpvObject::loadFile(QVariant 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);
|
||||
|
|
|
@ -135,6 +135,7 @@ public slots:
|
|||
void stepForward();
|
||||
void seek(double pos);
|
||||
void loadFile(QVariant urls);
|
||||
void screenshotToFile(QUrl url);
|
||||
void subAdd(QVariant urls);
|
||||
|
||||
bool enableAudio() const { return m_enableAudio; }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Type=Application
|
||||
Name=present
|
||||
GenericName=Church Presentation
|
||||
Comment=A Kirigami base church presenter
|
||||
Comment=A Kirigami based church presenter
|
||||
Exec=present %U
|
||||
TryExec=present
|
||||
Icon=present
|
||||
|
|
|
@ -26,28 +26,28 @@ Kirigami.ApplicationWindow {
|
|||
pageStack.initialPage: mainPage
|
||||
header: Presenter.Header {}
|
||||
|
||||
menuBar: Controls.MenuBar {
|
||||
Controls.Menu {
|
||||
title: qsTr("File")
|
||||
Controls.MenuItem { text: qsTr("New...") }
|
||||
Controls.MenuItem { text: qsTr("Open...") }
|
||||
Controls.MenuItem { text: qsTr("Save") }
|
||||
Controls.MenuItem { text: qsTr("Save As...") }
|
||||
Controls.MenuSeparator { }
|
||||
Controls.MenuItem { text: qsTr("Quit") }
|
||||
}
|
||||
Controls.Menu {
|
||||
title: qsTr("Settings")
|
||||
Controls.MenuItem {
|
||||
text: qsTr("Configure")
|
||||
onTriggered: openSettings()
|
||||
}
|
||||
}
|
||||
Controls.Menu {
|
||||
title: qsTr("Help")
|
||||
Controls.MenuItem { text: qsTr("About") }
|
||||
}
|
||||
}
|
||||
/* menuBar: Qt.platform.os !== "linux" ? Controls.MenuBar { */
|
||||
/* Controls.Menu { */
|
||||
/* title: qsTr("File") */
|
||||
/* Controls.MenuItem { text: qsTr("New...") } */
|
||||
/* Controls.MenuItem { text: qsTr("Open...") } */
|
||||
/* Controls.MenuItem { text: qsTr("Save") } */
|
||||
/* Controls.MenuItem { text: qsTr("Save As...") } */
|
||||
/* Controls.MenuSeparator { } */
|
||||
/* Controls.MenuItem { text: qsTr("Quit") } */
|
||||
/* } */
|
||||
/* Controls.Menu { */
|
||||
/* title: qsTr("Settings") */
|
||||
/* Controls.MenuItem { */
|
||||
/* text: qsTr("Configure") */
|
||||
/* onTriggered: openSettings() */
|
||||
/* } */
|
||||
/* } */
|
||||
/* Controls.Menu { */
|
||||
/* title: qsTr("Help") */
|
||||
/* Controls.MenuItem { text: qsTr("About") } */
|
||||
/* } */
|
||||
/* } : null */
|
||||
|
||||
Labs.MenuBar {
|
||||
Labs.Menu {
|
||||
|
@ -81,7 +81,7 @@ Kirigami.ApplicationWindow {
|
|||
|
||||
function toggleEditMode() {
|
||||
editMode = !editMode;
|
||||
mainPage.editSwitch(editMode);
|
||||
mainPage.editSwitch();
|
||||
}
|
||||
|
||||
function toggleLibrary() {
|
||||
|
|
230
src/qml/presenter/ImageEditor.qml
Normal file
230
src/qml/presenter/ImageEditor.qml
Normal file
|
@ -0,0 +1,230 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Dialogs 1.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import org.kde.kirigami 2.13 as Kirigami
|
||||
import "./" as Presenter
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
GridLayout {
|
||||
id: mainLayout
|
||||
anchors.fill: parent
|
||||
columns: 2
|
||||
rowSpacing: 5
|
||||
columnSpacing: 0
|
||||
|
||||
Controls.ToolBar {
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: 2
|
||||
id: toolbar
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Controls.ComboBox {
|
||||
model: Qt.fontFamilies()
|
||||
implicitWidth: 300
|
||||
editable: true
|
||||
hoverEnabled: true
|
||||
onCurrentTextChanged: showPassiveNotification(currentText)
|
||||
}
|
||||
Controls.SpinBox {
|
||||
editable: true
|
||||
from: 5
|
||||
to: 72
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ComboBox {
|
||||
model: ["IMAGES", "Center", "Right", "Justify"]
|
||||
implicitWidth: 100
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ToolButton {
|
||||
text: "B"
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ToolButton {
|
||||
text: "I"
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ToolButton {
|
||||
text: "U"
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ToolSeparator {}
|
||||
Item { Layout.fillWidth: true }
|
||||
Controls.ToolSeparator {}
|
||||
Controls.ToolButton {
|
||||
text: "Effects"
|
||||
icon.name: "image-auto-adjust"
|
||||
hoverEnabled: true
|
||||
onClicked: {}
|
||||
}
|
||||
Controls.ToolButton {
|
||||
id: backgroundButton
|
||||
text: "Background"
|
||||
icon.name: "fileopen"
|
||||
hoverEnabled: true
|
||||
onClicked: backgroundType.open()
|
||||
}
|
||||
|
||||
Controls.Popup {
|
||||
id: backgroundType
|
||||
x: backgroundButton.x
|
||||
y: backgroundButton.y + backgroundButton.height + 20
|
||||
modal: true
|
||||
focus: true
|
||||
dim: false
|
||||
background: Rectangle {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Tooltip
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
radius: 10
|
||||
border.color: Kirigami.Theme.activeBackgroundColor
|
||||
border.width: 2
|
||||
}
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
Controls.ToolButton {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
text: "Video"
|
||||
icon.name: "emblem-videos-symbolic"
|
||||
onClicked: videoFileDialog.open() & backgroundType.close()
|
||||
}
|
||||
Controls.ToolButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
text: "Image"
|
||||
icon.name: "folder-pictures-symbolic"
|
||||
onClicked: imageFileDialog.open() & backgroundType.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controls.SplitView {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: 2
|
||||
handle: Item{
|
||||
implicitWidth: 6
|
||||
Rectangle {
|
||||
height: parent.height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 1
|
||||
color: Controls.SplitHandle.hovered ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Controls.SplitView.fillHeight: true
|
||||
Controls.SplitView.preferredWidth: 500
|
||||
Controls.SplitView.minimumWidth: 500
|
||||
|
||||
Controls.TextField {
|
||||
id: songTitleField
|
||||
|
||||
Layout.preferredWidth: 300
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 20
|
||||
Layout.rightMargin: 20
|
||||
|
||||
placeholderText: "Song Title..."
|
||||
text: songTitle
|
||||
padding: 10
|
||||
onEditingFinished: updateTitle(text);
|
||||
}
|
||||
Controls.TextField {
|
||||
id: songVorderField
|
||||
|
||||
Layout.preferredWidth: 300
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 20
|
||||
Layout.rightMargin: 20
|
||||
|
||||
placeholderText: "verse order..."
|
||||
text: songVorder
|
||||
padding: 10
|
||||
onEditingFinished: updateVerseOrder(text);
|
||||
}
|
||||
|
||||
Controls.ScrollView {
|
||||
id: songLyricsField
|
||||
|
||||
Layout.preferredHeight: 3000
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 20
|
||||
|
||||
rightPadding: 20
|
||||
|
||||
Controls.TextArea {
|
||||
id: lyricsEditor
|
||||
width: parent.width
|
||||
placeholderText: "Put lyrics here..."
|
||||
persistentSelection: true
|
||||
text: songLyrics
|
||||
textFormat: TextEdit.MarkdownText
|
||||
padding: 10
|
||||
onEditingFinished: {
|
||||
updateLyrics(text);
|
||||
editorTimer.running = false;
|
||||
}
|
||||
onPressed: editorTimer.running = true
|
||||
}
|
||||
}
|
||||
Controls.TextField {
|
||||
id: songAuthorField
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 300
|
||||
Layout.leftMargin: 20
|
||||
Layout.rightMargin: 20
|
||||
|
||||
placeholderText: "Author..."
|
||||
text: songAuthor
|
||||
padding: 10
|
||||
onEditingFinished: updateAuthor(text)
|
||||
}
|
||||
|
||||
}
|
||||
ColumnLayout {
|
||||
Controls.SplitView.fillHeight: true
|
||||
Controls.SplitView.preferredWidth: 700
|
||||
Controls.SplitView.minimumWidth: 300
|
||||
|
||||
Rectangle {
|
||||
id: slideBar
|
||||
color: Kirigami.Theme.highlightColor
|
||||
|
||||
Layout.preferredWidth: 500
|
||||
Layout.preferredHeight: songTitleField.height
|
||||
Layout.rightMargin: 20
|
||||
Layout.leftMargin: 20
|
||||
}
|
||||
|
||||
Presenter.SlideEditor {
|
||||
id: slideEditor
|
||||
Layout.preferredWidth: 500
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: slideEditor.width / 16 * 9
|
||||
Layout.bottomMargin: 30
|
||||
Layout.rightMargin: 20
|
||||
Layout.leftMargin: 20
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Timer {
|
||||
id: editorTimer
|
||||
interval: 1000
|
||||
repeat: true
|
||||
running: false
|
||||
onTriggered: updateLyrics(lyricsEditor.text)
|
||||
}
|
||||
}
|
|
@ -109,6 +109,7 @@ ColumnLayout {
|
|||
showPassiveNotification(serviceItemList.currentIndex);
|
||||
changeSlideBackground(background, backgroundType);
|
||||
changeSlideText(text);
|
||||
changeSlideType(type);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.0 as Controls
|
||||
import QtQuick.Layouts 1.2
|
||||
import Qt.labs.platform 1.1 as Labs
|
||||
import org.kde.kirigami 2.13 as Kirigami
|
||||
import "./" as Presenter
|
||||
import org.presenter 1.0
|
||||
import mpv 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string selectedLibrary: "songs"
|
||||
property bool overlay: false
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
|
||||
|
@ -292,10 +295,37 @@ Item {
|
|||
opacity: 1.0
|
||||
|
||||
Controls.Label {
|
||||
id: videoLabel
|
||||
anchors.centerIn: parent
|
||||
text: "Videos"
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
id: videoCount
|
||||
anchors {left: videoLabel.right
|
||||
verticalCenter: videoLabel.verticalCenter
|
||||
leftMargin: 15}
|
||||
text: videosqlmodel.rowCount()
|
||||
font.pixelSize: 15
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
id: videoDrawerArrow
|
||||
anchors {right: parent.right
|
||||
verticalCenter: videoCount.verticalCenter
|
||||
rightMargin: 10}
|
||||
source: "arrow-down"
|
||||
rotation: selectedLibrary == "videos" ? 0 : 180
|
||||
|
||||
Behavior on rotation {
|
||||
NumberAnimation {
|
||||
easing.type: Easing.OutCubic
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
|
@ -313,6 +343,9 @@ Item {
|
|||
Layout.preferredHeight: parent.height - 200
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
model: videosqlmodel
|
||||
delegate: videoDelegate
|
||||
clip: true
|
||||
state: "deselected"
|
||||
|
||||
states: [
|
||||
|
@ -339,6 +372,109 @@ Item {
|
|||
duration: 300
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: videoDelegate
|
||||
Item{
|
||||
implicitWidth: ListView.view.width
|
||||
height: selectedLibrary == "videos" ? 50 : 0
|
||||
Kirigami.BasicListItem {
|
||||
id: videoListItem
|
||||
|
||||
property bool rightMenu: false
|
||||
|
||||
implicitWidth: videoLibraryList.width
|
||||
height: selectedLibrary == "videos" ? 50 : 0
|
||||
clip: true
|
||||
label: title
|
||||
/* subtitle: author */
|
||||
supportsMouseEvents: false
|
||||
backgroundColor: {
|
||||
if (parent.ListView.isCurrentItem) {
|
||||
Kirigami.Theme.highlightColor;
|
||||
} else if (videoDragHandler.containsMouse){
|
||||
Kirigami.Theme.highlightColor;
|
||||
} else {
|
||||
Kirigami.Theme.backgroundColor;
|
||||
}
|
||||
}
|
||||
textColor: {
|
||||
if (parent.ListView.isCurrentItem || videoDragHandler.containsMouse)
|
||||
activeTextColor;
|
||||
else
|
||||
Kirigami.Theme.textColor;
|
||||
}
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
easing.type: Easing.OutCubic
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
Drag.active: videoDragHandler.drag.active
|
||||
Drag.hotSpot.x: width / 2
|
||||
Drag.hotSpot.y: height / 2
|
||||
Drag.keys: [ "library" ]
|
||||
|
||||
states: State {
|
||||
name: "dragged"
|
||||
when: videoListItem.Drag.active
|
||||
PropertyChanges {
|
||||
target: videoListItem
|
||||
x: x
|
||||
y: y
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: videoDragHandler
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
drag {
|
||||
target: videoListItem
|
||||
onActiveChanged: {
|
||||
if (videoDragHandler.drag.active) {
|
||||
dragVideoTitle = title
|
||||
showPassiveNotification(dragVideoTitle)
|
||||
} else {
|
||||
videoListItem.Drag.drop()
|
||||
}
|
||||
}
|
||||
filterChildren: true
|
||||
threshold: 10
|
||||
}
|
||||
MouseArea {
|
||||
id: videoClickHandler
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: {
|
||||
if(mouse.button == Qt.RightButton)
|
||||
rightClickVideoMenu.popup()
|
||||
else{
|
||||
videoLibraryList.currentIndex = index
|
||||
const video = videosqlmodel.getVideo(videoLibraryList.currentIndex);
|
||||
/* showPassiveNotification("selected video: " + video); */
|
||||
if (!editMode)
|
||||
editMode = true;
|
||||
editSwitch("video", video);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Controls.Menu {
|
||||
id: rightClickVideoMenu
|
||||
x: videoClickHandler.mouseX
|
||||
y: videoClickHandler.mouseY + 10
|
||||
Kirigami.Action {
|
||||
text: "delete"
|
||||
onTriggered: videosqlmodel.deleteVideo(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -513,5 +649,59 @@ Item {
|
|||
|
||||
}
|
||||
}
|
||||
DropArea {
|
||||
id: fileDropArea
|
||||
anchors.fill: parent
|
||||
onDropped: drop => {
|
||||
overlay = false;
|
||||
showPassiveNotification("dropped");
|
||||
print(drop.urls);
|
||||
/* thumbnailer.loadFile(drop.urls[0]); */
|
||||
addVideo(drop.urls[0]);
|
||||
}
|
||||
onEntered: overlay = true
|
||||
onExited: overlay = false
|
||||
|
||||
function addVideo(url) {
|
||||
videosqlmodel.newVideo(url);
|
||||
selectedLibrary = "videos";
|
||||
videoLibraryList.currentIndex = videosqlmodel.rowCount();
|
||||
print(videosqlmodel.getVideo(videoLibraryList.currentIndex));
|
||||
const video = videosqlmodel.getVideo(videoLibraryList.currentIndex);
|
||||
showPassiveNotification("newest video: " + video);
|
||||
if (!editMode)
|
||||
editMode = true;
|
||||
editSwitch("video", video);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: fileDropOverlay
|
||||
color: overlay ? Kirigami.Theme.highlightColor : "#00000000"
|
||||
anchors.fill: parent
|
||||
border.width: 8
|
||||
border.color: overlay ? Kirigami.Theme.hoverColor : "#00000000"
|
||||
}
|
||||
|
||||
MpvObject {
|
||||
id: thumbnailer
|
||||
useHwdec: true
|
||||
enableAudio: false
|
||||
width: 0
|
||||
height: 0
|
||||
Component.onCompleted: print("ready")
|
||||
onFileLoaded: {
|
||||
thumbnailer.pause();
|
||||
print("FILE: " + thumbnailer.mediaTitle);
|
||||
thumbnailer.screenshotToFile(thumbnailFile(thumbnailer.mediaTitle));
|
||||
showPassiveNotification("Screenshot Taken to: " + thumbnailFile(thumbnailer.mediaTitle));
|
||||
thumbnailer.stop();
|
||||
}
|
||||
function thumbnailFile(title) {
|
||||
const thumbnailFolder = Labs.StandardPaths.writableLocation(Labs.StandardPaths.AppDataLocation) + "/thumbnails/";
|
||||
return Qt.resolvedUrl(thumbnailFolder + title);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ Controls.Page {
|
|||
property string songVorder: ""
|
||||
property int blurRadius: 0
|
||||
|
||||
/* property var video */
|
||||
|
||||
property string dragSongTitle: ""
|
||||
|
||||
property bool editing: true
|
||||
|
@ -78,6 +80,20 @@ Controls.Page {
|
|||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: videoEditorComp
|
||||
Presenter.VideoEditor {
|
||||
id: videoEditor
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: imageEditorComp
|
||||
Presenter.ImageEditor {
|
||||
id: imageEditor
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: presentLoader
|
||||
active: presenting
|
||||
|
@ -152,6 +168,19 @@ Controls.Page {
|
|||
id: songsqlmodel
|
||||
}
|
||||
|
||||
VideoSqlModel {
|
||||
id: videosqlmodel
|
||||
}
|
||||
|
||||
function changeSlideType(type) {
|
||||
/* showPassiveNotification("used to be: " + presentation.text); */
|
||||
presentation.itemType = type;
|
||||
/* showPassiveNotification("next"); */
|
||||
if (slideItem)
|
||||
slideItem.itemType = type;
|
||||
/* showPassiveNotification("last"); */
|
||||
}
|
||||
|
||||
function changeSlideText(text) {
|
||||
/* showPassiveNotification("used to be: " + presentation.text); */
|
||||
presentation.text = text;
|
||||
|
@ -193,11 +222,24 @@ Controls.Page {
|
|||
showPassiveNotification("previous slide please")
|
||||
}
|
||||
|
||||
function editSwitch(edit) {
|
||||
if (edit)
|
||||
mainPageArea.push(songEditorComp, Controls.StackView.Immediate)
|
||||
else
|
||||
mainPageArea.pop(Controls.StackView.Immediate)
|
||||
function editSwitch(editType, item) {
|
||||
if (editMode) {
|
||||
switch (editType) {
|
||||
case "song" :
|
||||
mainPageArea.push(songEditorComp, Controls.StackView.Immediate);
|
||||
break;
|
||||
case "video" :
|
||||
mainPageArea.push(videoEditorComp, {"video": item}, Controls.StackView.Immediate);
|
||||
break;
|
||||
case "image" :
|
||||
mainPageArea.push(imageEditorComp, Controls.StackView.Immediate);
|
||||
break;
|
||||
default:
|
||||
mainPageArea.pop(Controls.StackView.Immediate);
|
||||
editMode = false;
|
||||
}
|
||||
} else
|
||||
mainPageArea.pop(Controls.StackView.Immediate);
|
||||
}
|
||||
|
||||
function present(present) {
|
||||
|
|
|
@ -11,6 +11,7 @@ Item {
|
|||
id: root
|
||||
|
||||
property string text
|
||||
property string itemType
|
||||
property url imagebackground
|
||||
property url vidbackground
|
||||
|
||||
|
@ -80,6 +81,7 @@ Item {
|
|||
Layout.alignment: Qt.AlignCenter
|
||||
textSize: width / 15
|
||||
text: root.text
|
||||
itemType: root.itemType
|
||||
imageSource: imagebackground
|
||||
videoSource: vidbackground
|
||||
preview: true
|
||||
|
|
|
@ -45,10 +45,10 @@ Item {
|
|||
enableAudio: !preview
|
||||
Component.onCompleted: mpvLoadingTimer.start()
|
||||
onFileLoaded: {
|
||||
print(videoSource + " has been loaded");
|
||||
showPassiveNotification(videoSource + " has been loaded");
|
||||
if (itemType == "song")
|
||||
mpv.setProperty("loop", "inf");
|
||||
print(mpv.getProperty("loop"));
|
||||
showPassiveNotification(mpv.getProperty("loop"));
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
|
227
src/qml/presenter/VideoEditor.qml
Normal file
227
src/qml/presenter/VideoEditor.qml
Normal file
|
@ -0,0 +1,227 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Dialogs 1.3
|
||||
import QtQuick.Layouts 1.2
|
||||
import org.kde.kirigami 2.13 as Kirigami
|
||||
import "./" as Presenter
|
||||
import mpv 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var video
|
||||
property bool audioOn: true
|
||||
|
||||
GridLayout {
|
||||
id: mainLayout
|
||||
anchors.fill: parent
|
||||
columns: 2
|
||||
rowSpacing: 5
|
||||
columnSpacing: 0
|
||||
|
||||
Controls.ToolBar {
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: 2
|
||||
id: toolbar
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Controls.ComboBox {
|
||||
model: Qt.fontFamilies()
|
||||
implicitWidth: 300
|
||||
editable: true
|
||||
hoverEnabled: true
|
||||
onCurrentTextChanged: showPassiveNotification(currentText)
|
||||
}
|
||||
Controls.SpinBox {
|
||||
editable: true
|
||||
from: 5
|
||||
to: 72
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ComboBox {
|
||||
model: ["VIDEOS", "Center", "Right", "Justify"]
|
||||
implicitWidth: 100
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ToolButton {
|
||||
text: "B"
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ToolButton {
|
||||
text: "I"
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ToolButton {
|
||||
text: "U"
|
||||
hoverEnabled: true
|
||||
}
|
||||
Controls.ToolSeparator {}
|
||||
Item { Layout.fillWidth: true }
|
||||
Controls.ToolSeparator {}
|
||||
Controls.ToolButton {
|
||||
text: "Effects"
|
||||
icon.name: "image-auto-adjust"
|
||||
hoverEnabled: true
|
||||
onClicked: {}
|
||||
}
|
||||
Controls.ToolButton {
|
||||
id: backgroundButton
|
||||
text: "Background"
|
||||
icon.name: "fileopen"
|
||||
hoverEnabled: true
|
||||
onClicked: backgroundType.open()
|
||||
}
|
||||
|
||||
Controls.Popup {
|
||||
id: backgroundType
|
||||
x: backgroundButton.x
|
||||
y: backgroundButton.y + backgroundButton.height + 20
|
||||
modal: true
|
||||
focus: true
|
||||
dim: false
|
||||
background: Rectangle {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Tooltip
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
radius: 10
|
||||
border.color: Kirigami.Theme.activeBackgroundColor
|
||||
border.width: 2
|
||||
}
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
Controls.ToolButton {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
text: "Video"
|
||||
icon.name: "emblem-videos-symbolic"
|
||||
onClicked: videoFileDialog.open() & backgroundType.close()
|
||||
}
|
||||
Controls.ToolButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
text: "Image"
|
||||
icon.name: "folder-pictures-symbolic"
|
||||
onClicked: imageFileDialog.open() & backgroundType.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controls.SplitView {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: 2
|
||||
handle: Item{
|
||||
implicitWidth: 6
|
||||
Rectangle {
|
||||
height: parent.height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 1
|
||||
color: Controls.SplitHandle.hovered ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Controls.SplitView.fillHeight: true
|
||||
Controls.SplitView.preferredWidth: 300
|
||||
Controls.SplitView.minimumWidth: 100
|
||||
|
||||
Controls.TextField {
|
||||
id: videoTitleField
|
||||
|
||||
Layout.preferredWidth: 300
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 20
|
||||
Layout.rightMargin: 20
|
||||
|
||||
placeholderText: "Song Title..."
|
||||
text: video[0]
|
||||
padding: 10
|
||||
/* onEditingFinished: updateTitle(text); */
|
||||
}
|
||||
|
||||
Item {
|
||||
id: empty
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Controls.SplitView.fillHeight: true
|
||||
Controls.SplitView.preferredWidth: 700
|
||||
Controls.SplitView.minimumWidth: 300
|
||||
spacing: 5
|
||||
|
||||
Item {
|
||||
id: topEmpty
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
MpvObject {
|
||||
id: videoPreview
|
||||
objectName: "mpv"
|
||||
Layout.preferredWidth: 600
|
||||
Layout.preferredHeight: Layout.preferredWidth / 16 * 9
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
useHwdec: true
|
||||
enableAudio: audioOn
|
||||
Component.onCompleted: mpvLoadingTimer.start()
|
||||
onPositionChanged: videoSlider.value = position
|
||||
onFileLoaded: {
|
||||
showPassiveNotification(video.title + " has been loaded");
|
||||
videoPreview.pause();
|
||||
/* showPassiveNotification(mpv.getProperty("loop")); */
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: videoBg
|
||||
color: Kirigami.Theme.alternateBackgroundColor
|
||||
|
||||
Layout.preferredWidth: videoPreview.Layout.preferredWidth
|
||||
Layout.preferredHeight: videoTitleField.height
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 2
|
||||
Kirigami.Icon {
|
||||
source: videoPreview.isPlaying ? "media-pause" : "media-play"
|
||||
Layout.preferredWidth: 25
|
||||
Layout.preferredHeight: 25
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onPressed: videoPreview.playPause()
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
Controls.Slider {
|
||||
id: videoSlider
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 25
|
||||
from: 0
|
||||
to: videoPreview.duration
|
||||
/* value: videoPreview.postion */
|
||||
live: false
|
||||
onMoved: videoPreview.seek(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: botEmpty
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: mpvLoadingTimer
|
||||
interval: 100
|
||||
onTriggered: {
|
||||
videoPreview.loadFile(video[1].toString());
|
||||
/* showPassiveNotification(video[0]); */
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
<file>qml/presenter/Actions.qml</file>
|
||||
<file>qml/presenter/PanelItem.qml</file>
|
||||
<file>qml/presenter/SongEditor.qml</file>
|
||||
<file>qml/presenter/VideoEditor.qml</file>
|
||||
<file>qml/presenter/ImageEditor.qml</file>
|
||||
<file>qml/presenter/Slide.qml</file>
|
||||
<file>qml/presenter/SlideEditor.qml</file>
|
||||
<file>qml/presenter/DragHandle.qml</file>
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
#include "songlistmodel.h"
|
||||
#include <QTextStream>
|
||||
#include <QDebug>
|
||||
|
||||
SongListModel::SongListModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
m_data
|
||||
<< Data("10,000 Reasons", "10,000 reasons for my heart to sing", "Matt Redman", "13470183", "")
|
||||
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")
|
||||
<< Data("10,000 Reasons", "10,000 reasons for my heart to sing", "Matt Redman", "13470183", "")
|
||||
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")
|
||||
<< Data("River", "I'm going down to the river", "Jordan Feliz", "13470183", "")
|
||||
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")
|
||||
<< Data("10,000 Reasons", "10,000 reasons for my heart to sing", "Matt Redman", "13470183", "")
|
||||
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")
|
||||
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")
|
||||
<< Data("10,000 Reasons", "10,000 reasons for my heart to sing", "Matt Redman", "13470183", "")
|
||||
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "")
|
||||
<< Data("10,000 Reasons", "10,000 reasons for my heart to sing", "Matt Redman", "13470183", "")
|
||||
<< Data("Marvelous Light", "Into marvelous light I'm running", "Chris Tomlin", "13470183", "");
|
||||
}
|
||||
|
||||
int SongListModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
return m_data.count();
|
||||
}
|
||||
|
||||
QVariant SongListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
// this is the returning of song data
|
||||
const Data &data = m_data.at(index.row());
|
||||
if ( role == TitleRole )
|
||||
return data.title;
|
||||
else if (role == LyricsRole)
|
||||
return data.lyrics;
|
||||
else if (role == AuthorRole)
|
||||
return data.author;
|
||||
else if (role == CCLINumRole)
|
||||
return data.ccli;
|
||||
else if (role == AudioRole)
|
||||
return data.audio;
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> SongListModel::roleNames() const
|
||||
{
|
||||
static QHash<int, QByteArray> mapping {
|
||||
{TitleRole, "title"},
|
||||
{LyricsRole, "lyrics"},
|
||||
{AuthorRole, "author"},
|
||||
{CCLINumRole, "ccli"},
|
||||
{AudioRole, "audio"}
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
void SongListModel::lyricsSlides(QString lyrics)
|
||||
{
|
||||
QTextStream stream(&lyrics);
|
||||
QString line = stream.readLine();
|
||||
qDebug() << line;
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef SONGLISTMODEL_H
|
||||
#define SONGLISTMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
struct Data {
|
||||
Data () {}
|
||||
Data ( const QString& title, const QString& lyrics, const QString& author,
|
||||
const QString& ccli, const QString& audio)
|
||||
: title(title), lyrics(lyrics), author(author), ccli(ccli), audio(audio) {}
|
||||
QString title;
|
||||
QString lyrics;
|
||||
QString author;
|
||||
QString ccli;
|
||||
QString audio;
|
||||
};
|
||||
|
||||
class SongListModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
TitleRole = Qt::UserRole,
|
||||
LyricsRole,
|
||||
AuthorRole,
|
||||
CCLINumRole,
|
||||
AudioRole,
|
||||
};
|
||||
explicit SongListModel(QObject *parent = nullptr);
|
||||
|
||||
// Basic functionality:
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
|
||||
private:
|
||||
QVector< Data > m_data;
|
||||
|
||||
public slots:
|
||||
void lyricsSlides(QString lyrics);
|
||||
};
|
||||
|
||||
#endif // SONGLISTMODEL_H
|
|
@ -92,9 +92,9 @@ void SongSqlModel::newSong() {
|
|||
|
||||
if (insertRecord(rows, recorddata)) {
|
||||
submitAll();
|
||||
}else {
|
||||
} else {
|
||||
qDebug() << lastError();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void SongSqlModel::deleteSong(const int &row) {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define SONGSQLMODEL_H
|
||||
|
||||
#include <QSqlTableModel>
|
||||
#include <qabstractitemmodel.h>
|
||||
#include <qobjectdefs.h>
|
||||
#include <qqml.h>
|
||||
#include <qvariant.h>
|
||||
|
|
167
src/videosqlmodel.cpp
Normal file
167
src/videosqlmodel.cpp
Normal file
|
@ -0,0 +1,167 @@
|
|||
#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,"
|
||||
" 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();
|
||||
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";
|
||||
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);
|
||||
|
||||
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;
|
||||
QSqlRecord rowdata = record(row);
|
||||
qDebug() << rowdata;
|
||||
rowdata.setValue("title", title);
|
||||
setRecord(row, 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;
|
||||
QSqlRecord rowdata = record(row);
|
||||
qDebug() << rowdata;
|
||||
rowdata.setValue("filePath", filePath);
|
||||
setRecord(row, rowdata);
|
||||
qDebug() << rowdata;
|
||||
submitAll();
|
||||
emit filePathChanged();
|
||||
}
|
||||
|
||||
QVariantList VideoSqlModel::getVideo(const int &row) {
|
||||
qDebug() << "Row we are getting is " << row;
|
||||
QVariantList video;
|
||||
QSqlRecord rec = record(row - 1);
|
||||
qDebug() << rec.value("title");
|
||||
video.append(rec.value("title"));
|
||||
video.append(rec.value("filePath"));
|
||||
return video;
|
||||
}
|
49
src/videosqlmodel.h
Normal file
49
src/videosqlmodel.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#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)
|
||||
QML_ELEMENT
|
||||
|
||||
public:
|
||||
VideoSqlModel(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 newVideo(const QUrl &filePath);
|
||||
Q_INVOKABLE void deleteVideo(const int &row);
|
||||
Q_INVOKABLE QVariantList getVideo(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 //SONGSQLMODEL_H
|
Loading…
Add table
Add a link
Reference in a new issue