Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+107
@@ -0,0 +1,107 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
# Created by Mike Wille, SLSE, 2016-03-17
|
||||
#
|
||||
|
||||
add_library(ExportBdvTopicSetting INTERFACE)
|
||||
target_compile_definitions(ExportBdvTopicSetting INTERFACE EXPORT_BDV_TOPIC_AS_JSON)
|
||||
|
||||
SET(pre_script_gui_SRCS
|
||||
pre_journal_dialog.h
|
||||
pre_journal_dialog.cpp
|
||||
pre_journal_types.h
|
||||
pre_journal_types.cpp
|
||||
pre_screen.h
|
||||
pre_screen.cpp
|
||||
pre_screen_recorder.h
|
||||
pre_screen_recorder.cpp
|
||||
pre_screen_shotter.h
|
||||
pre_screen_shotter.cpp
|
||||
pre_script_timeline_gui_plugin.cpp
|
||||
pre_script_timeline_gui_plugin.h
|
||||
pre_script_delegate.cpp
|
||||
pre_script_delegate.h
|
||||
pre_script_dialog.h
|
||||
pre_script_dialog.cpp
|
||||
pre_script_dialog_drag_handler.cpp
|
||||
pre_script_dialog_drag_handler.h
|
||||
pre_script_dialog_resize_handler.cpp
|
||||
pre_script_dialog_resize_handler.h
|
||||
pre_script_dialog_select_handler.cpp
|
||||
pre_script_dialog_select_handler.h
|
||||
pre_script_model.cpp
|
||||
pre_script_model.h
|
||||
pre_topic_recorder.cpp
|
||||
pre_topic_recorder.h
|
||||
pre_topic_shotter.cpp
|
||||
pre_topic_shotter.h
|
||||
pre_storyboard.cpp
|
||||
pre_storyboard.h
|
||||
pre_topic_item.cpp
|
||||
pre_topic_item.h
|
||||
pre_topic_track.cpp
|
||||
pre_topic_track.h
|
||||
pre_utils.cpp
|
||||
pre_utils.h
|
||||
)
|
||||
|
||||
QT5_WRAP_UI(pre_script_gui_SRCS
|
||||
pre_storyboard.ui
|
||||
pre_script_dialog.ui
|
||||
pre_journal_dialog.ui
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY(WS PreScriptTimelineGuiPlugin MODULE ${pre_script_gui_SRCS})
|
||||
NGL_TARGET_COMPILE_DEFINITIONS(PreScriptTimelineGuiPlugin PRIVATE L=QStringLiteral)
|
||||
NGL_TARGET_LINK_LIBRARIES(PreScriptTimelineGuiPlugin
|
||||
NGLLogger
|
||||
LtsSession
|
||||
PreDataPluginInterface
|
||||
PreScriptTimelineGuiPluginInterface
|
||||
PreGuiCommon
|
||||
PreGuiScriptCommon
|
||||
LtsTimeline
|
||||
svcPreCommon
|
||||
Qt5::Widgets
|
||||
LtsWidgets
|
||||
LtsDataView
|
||||
LtsSessionData
|
||||
ProjectConfig
|
||||
LtsScriptTimeline
|
||||
LtsUtilsCoreHeaderOnly
|
||||
LtsMultiMonitor
|
||||
Thp2Common
|
||||
|
||||
LtsAppManager # start replay app
|
||||
LtsSessionData # set partition preset for replay app
|
||||
TopicEditorInterface
|
||||
TopicEditorBase
|
||||
LtsPluginManager # manipulate apps
|
||||
|
||||
# for Topics
|
||||
EvlGuiPluginInterface
|
||||
SrmDataPluginInterface # start playback
|
||||
LtsPrinting
|
||||
SituationMapGuiPluginInterface
|
||||
|
||||
# Script Player Controls:
|
||||
SrpGuiPluginInterface
|
||||
|
||||
# file topic
|
||||
FlmExplorerGuiPluginInterface
|
||||
# building topic
|
||||
BdvViewerGuiPluginInterface
|
||||
# report topic
|
||||
RepGuiPluginInterface
|
||||
LtsReportDisplay
|
||||
MrkCommon # for ApplyFilter @ SitmapFilter
|
||||
LtsUtilsSettings
|
||||
ngltools # DTG
|
||||
situationMap # set time string on map
|
||||
ExportBdvTopicSetting
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(PreScriptTimelineGuiPlugin)
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#include "pre_journal_dialog.h"
|
||||
#include "ui_pre_journal_dialog.h"
|
||||
|
||||
#include <LtsPrinting/printing.h>
|
||||
#include <Services/PresentationManagement/logger.h>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
JournalDialog::JournalDialog(LtsSession::LtsSession *session, QWidget *parent)
|
||||
: LtsWidgets::LtsDialog(parent)
|
||||
, m_session(session)
|
||||
, m_ui(new Ui::JournalDialog)
|
||||
, m_logger(getLogger(QStringLiteral("JournalDialog")))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &JournalDialog::onPrint);
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Journal"));
|
||||
}
|
||||
|
||||
|
||||
JournalDialog::~JournalDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param journal
|
||||
* @return bool
|
||||
*/
|
||||
bool JournalDialog::init(MainJournal *journal)
|
||||
{
|
||||
m_journal = journal;
|
||||
return true;
|
||||
}
|
||||
|
||||
void JournalDialog::retranslate()
|
||||
{
|
||||
translate(m_ui, tr("Script Journal"));
|
||||
fillJournal();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// PRIVATE
|
||||
|
||||
void JournalDialog::fillJournal()
|
||||
{
|
||||
m_ui->listWidget->clear();
|
||||
|
||||
if (!m_journal)
|
||||
return;
|
||||
|
||||
m_ui->listWidget->addItem(m_journal->getText());
|
||||
for (const Journal *j : qAsConst(m_journal->journalList))
|
||||
m_ui->listWidget->addItem(j->getText());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE SLOTS
|
||||
|
||||
void JournalDialog::onPrint()
|
||||
{
|
||||
LtsPrinting::printView(m_session, this, m_ui->listWidget);
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "pre_journal_types.h"
|
||||
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
#include <LtsSession/lts_session.h>
|
||||
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class JournalDialog;
|
||||
}
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class JournalDialog : public LtsWidgets::LtsDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit JournalDialog(LtsSession::LtsSession *session, QWidget *parent);
|
||||
~JournalDialog();
|
||||
|
||||
bool init(MainJournal *journal);
|
||||
|
||||
void retranslate() override final;
|
||||
|
||||
private:
|
||||
void fillJournal();
|
||||
|
||||
private slots:
|
||||
void onPrint();
|
||||
|
||||
private:
|
||||
LtsSession::LtsSession *m_session { nullptr };
|
||||
MainJournal *m_journal { nullptr };
|
||||
|
||||
Ui::JournalDialog *m_ui { nullptr };
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>JournalDialog</class>
|
||||
<widget class="QWidget" name="JournalDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>707</width>
|
||||
<height>464</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Script Editor</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listWidget">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
#include "pre_journal_types.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <ngltools/ngltools_dtg.h>
|
||||
#include <Common/LtsTimeline/timeline_utils.h>
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
QString timeSinceEpochInDTG(qint64 msecsSinceEpoch)
|
||||
{
|
||||
return LtsTimeline::DateTime::toString(msecsSinceEpoch,LtsTimeline::DateTime::DTG);
|
||||
}
|
||||
|
||||
|
||||
//QString presentationTimeToTime(int presentationTime)
|
||||
//{
|
||||
// return QTime::fromMSecsSinceStartOfDay(presentationTime * 1000).toString();
|
||||
//}
|
||||
|
||||
QString msecsToString(int presentationTime)
|
||||
{
|
||||
int milliseconds = presentationTime;
|
||||
int seconds = milliseconds / 1000;
|
||||
milliseconds = milliseconds % 1000;
|
||||
int minutes = seconds / 60;
|
||||
seconds = seconds % 60;
|
||||
int hours = minutes / 60;
|
||||
minutes = minutes % 60;
|
||||
|
||||
QTime time;
|
||||
time.setHMS(hours, minutes, seconds, milliseconds);
|
||||
|
||||
return time.toString(QStringLiteral("hh:mm:ss"));
|
||||
}
|
||||
|
||||
Journal::Journal()
|
||||
: m_started(QDateTime::currentMSecsSinceEpoch())
|
||||
{}
|
||||
|
||||
|
||||
QString Journal::started() const
|
||||
{
|
||||
return timeSinceEpochInDTG(m_started);
|
||||
}
|
||||
|
||||
|
||||
MainJournal::~MainJournal()
|
||||
{
|
||||
qDeleteAll(journalList);
|
||||
}
|
||||
|
||||
|
||||
QString MainJournal::getText() const
|
||||
{
|
||||
return Journal::tr("Script Player started on %1").arg(timeSinceEpochInDTG(started));
|
||||
}
|
||||
|
||||
|
||||
NextTopicJournal::NextTopicJournal(int presentationTime, bool isFilter, const QString &topicTypeName, int length)
|
||||
: Journal()
|
||||
, m_isFilter(isFilter)
|
||||
, m_topicTypeName(topicTypeName)
|
||||
, m_length(length)
|
||||
, m_presentationTime(presentationTime)
|
||||
{}
|
||||
|
||||
|
||||
QString NextTopicJournal::getText() const
|
||||
{
|
||||
return Journal::tr("%1 '%2' started at %3 for %4 seconds on %5").arg(m_isFilter ? tr("FilterTopic") : tr("Topic"),
|
||||
m_topicTypeName, msecsToString(m_presentationTime)).arg(m_length).arg(started());
|
||||
}
|
||||
|
||||
|
||||
QString PlayJournal::getText() const
|
||||
{
|
||||
return Journal::tr("Started / resumed script at %1 on %2").arg(msecsToString(m_presentationTime), started());
|
||||
}
|
||||
|
||||
|
||||
QString PauseJournal::getText() const
|
||||
{
|
||||
return Journal::tr("Paused script at %1 on %2").arg(msecsToString(m_presentationTime), started());
|
||||
}
|
||||
|
||||
|
||||
QString EndedJournal::getText() const
|
||||
{
|
||||
return Journal::tr("Script ended at %1 on %2").arg(msecsToString(m_presentationTime), started());
|
||||
}
|
||||
|
||||
|
||||
QString StopJournal::getText() const
|
||||
{
|
||||
return Journal::tr("Script stopped at %1 on %2").arg(msecsToString(m_presentationTime), started());
|
||||
}
|
||||
|
||||
|
||||
QString SetTimeJournal::getText() const
|
||||
{
|
||||
return Journal::tr("Position time set to %1 on %2").arg(msecsToString(m_toPresentationTime), started());
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QVector>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class Journal
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(Journal)
|
||||
|
||||
public:
|
||||
Journal();
|
||||
virtual ~Journal() = default;
|
||||
QString started() const;
|
||||
virtual QString getText() const = 0;
|
||||
qint64 m_started;
|
||||
};
|
||||
|
||||
|
||||
class MainJournal
|
||||
{
|
||||
public:
|
||||
~MainJournal();
|
||||
QString getText() const;
|
||||
|
||||
qint64 started = 0;
|
||||
QVector<Journal *> journalList;
|
||||
};
|
||||
|
||||
|
||||
class NextTopicJournal : public Journal
|
||||
{
|
||||
public:
|
||||
NextTopicJournal(int presentationTime, bool isFilter, /*int topicId, */const QString &topicTypeName, int length);
|
||||
QString getText() const override final;
|
||||
|
||||
bool m_isFilter;
|
||||
//int m_topicId;
|
||||
QString m_topicTypeName;
|
||||
int m_length;
|
||||
int m_presentationTime;
|
||||
};
|
||||
|
||||
|
||||
class PlayJournal : public Journal
|
||||
{
|
||||
public:
|
||||
explicit PlayJournal(int presentationTime)
|
||||
: Journal()
|
||||
, m_presentationTime(presentationTime)
|
||||
{}
|
||||
|
||||
QString getText() const override final;
|
||||
|
||||
int m_presentationTime;
|
||||
};
|
||||
|
||||
|
||||
class PauseJournal : public Journal
|
||||
{
|
||||
public:
|
||||
explicit PauseJournal(int presentationTime)
|
||||
: Journal()
|
||||
, m_presentationTime(presentationTime)
|
||||
{}
|
||||
|
||||
QString getText() const override final;
|
||||
|
||||
int m_presentationTime;
|
||||
};
|
||||
|
||||
|
||||
class EndedJournal : public Journal
|
||||
{
|
||||
public:
|
||||
explicit EndedJournal(int presentationTime)
|
||||
: Journal()
|
||||
, m_presentationTime(presentationTime)
|
||||
{}
|
||||
|
||||
QString getText() const override final;
|
||||
|
||||
int m_presentationTime;
|
||||
};
|
||||
|
||||
|
||||
class StopJournal : public Journal
|
||||
{
|
||||
public:
|
||||
explicit StopJournal(int presentationTime)
|
||||
: Journal()
|
||||
, m_presentationTime(presentationTime)
|
||||
{}
|
||||
|
||||
QString getText() const override final;
|
||||
|
||||
int m_presentationTime;
|
||||
};
|
||||
|
||||
|
||||
class SetTimeJournal : public Journal
|
||||
{
|
||||
public:
|
||||
explicit SetTimeJournal(int toPresentationTime)
|
||||
: Journal()
|
||||
, m_toPresentationTime(toPresentationTime)
|
||||
{}
|
||||
|
||||
QString getText() const override final;
|
||||
|
||||
int m_toPresentationTime;
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#include "pre_screen.h"
|
||||
|
||||
#include <LtsUtilsGui/LtsUtilsGuiDds/dds_vector_to_pixmap.h>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
|
||||
|
||||
Screen::Screen()
|
||||
{
|
||||
setBgColor(QColor("black"));
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
setWindowIcon(LtsUtils::iconFromSvg(QStringLiteral(":/style/execute_presentation.svg")));
|
||||
setWindowTitle(tr("Presentation Screen"));
|
||||
}
|
||||
|
||||
|
||||
Screen::~Screen()
|
||||
{
|
||||
const auto p1Widgets = findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
for (QWidget *w : p1Widgets)
|
||||
w->setParent(nullptr);
|
||||
}
|
||||
|
||||
|
||||
void Screen::setBgColor(const QColor &color)
|
||||
{
|
||||
setStyleSheet(QStringLiteral("Screen { background: %1; background-color: %1; }").arg(color.name()));
|
||||
}
|
||||
|
||||
|
||||
void Screen::paintEvent(QPaintEvent *) // needed to apply the style sheet...........
|
||||
{
|
||||
QStyleOption opt;
|
||||
opt.init(this);
|
||||
QPainter p(this);
|
||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <LtsMultiMonitor/lts_dummy_widget.h>
|
||||
|
||||
|
||||
class Screen : public LtsMultiMonitor::SecondaryWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Screen();
|
||||
virtual ~Screen();
|
||||
|
||||
void setBgColor(const QColor &color);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
};
|
||||
Executable
+146
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* class for recording topics on a screen
|
||||
* $Id:
|
||||
* $HeadURL:
|
||||
* $Author:
|
||||
* Created by Hannes Fritsche, SLTE, 2023-01-18
|
||||
*/
|
||||
|
||||
#include "pre_screen_recorder.h"
|
||||
#include "pre_screen.h"
|
||||
#include "pre_topic_item.h"
|
||||
#include <QProcess>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
ScreenRecorder::ScreenRecorder(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_logger(getLogger(QStringLiteral("ScreenRecorder")))
|
||||
{
|
||||
connect(this, &ScreenRecorder::newTime, this, [this](qint64 _t1){ DEBUG_LOG(m_logger) << "newTime: " << _t1; });
|
||||
}
|
||||
|
||||
|
||||
void ScreenRecorder::cut()
|
||||
{
|
||||
for (TopicRecorder *tr : qAsConst(m_topicRecorder))
|
||||
{
|
||||
if (tr->isFailed()) // skip, plus already in m_failedExportTopicIds by exportFailed signal
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
tr->cut();
|
||||
}
|
||||
catch (const QString &msg)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Cutting recording of topic '" << tr->topicName() << "', id: " << tr->topicId() << " failed! " << msg;
|
||||
m_failedExportTopicIds += tr->topicId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScreenRecorder::save(const QString &tmpDirPath)
|
||||
{
|
||||
for (TopicRecorder *tr : qAsConst(m_topicRecorder))
|
||||
{
|
||||
if (tr->isFailed()) // skip, plus already in m_failedExportTopicIds by exportFailed signal
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
tr->save(tmpDirPath);
|
||||
}
|
||||
catch (const QString &msg)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Saving recording of topic '" << tr->topicName() << "', id: " << tr->topicId() << " failed! " << msg;
|
||||
m_failedExportTopicIds += tr->topicId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScreenRecorder::addTopicsForScreen(Screen *screen, const std::vector<TopicItem> &topicItems)
|
||||
{
|
||||
m_topicRecorder.reserve(m_topicRecorder.size() + topicItems.size());
|
||||
const qint32 startTimeOffset = -10000;
|
||||
|
||||
for (const TopicItem ¤tTopicItem : topicItems)
|
||||
{
|
||||
const auto id = currentTopicItem.topicId();
|
||||
const qint64 startTime = currentTopicItem.startTime() + startTimeOffset;
|
||||
const qint64 stopTime = currentTopicItem.startTime() + currentTopicItem.duration();
|
||||
const QString name(currentTopicItem.channel().replace(QStringLiteral(" "), QStringLiteral("_")) + QString::number(currentTopicItem.id()));
|
||||
TopicRecorder *topicRecorder = new TopicRecorder(id, name, screen, startTimeOffset, startTime, stopTime,
|
||||
currentTopicItem.topicData().x, currentTopicItem.topicData().y,
|
||||
currentTopicItem.topicData().w, currentTopicItem.topicData().h, this);
|
||||
|
||||
connect(this, &ScreenRecorder::newTime, topicRecorder, &TopicRecorder::onNewTime);
|
||||
connect(this, &ScreenRecorder::cancel, topicRecorder, &TopicRecorder::cancel);
|
||||
connect(topicRecorder, &TopicRecorder::exportFailed, this, [this, id]() { emit topicExportFailed(id); });
|
||||
connect(topicRecorder, &TopicRecorder::exportFailed, this, [this, name, id]()
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Export failed for topic '" << name << "', id: " << id;
|
||||
m_failedExportTopicIds += id;
|
||||
});
|
||||
|
||||
m_topicRecorder.append(topicRecorder);
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "Added " << topicItems.size() << " TopicRecorder.";
|
||||
}
|
||||
|
||||
//void ScreenRecorder::saveRecordings(const QString &tmpDirPath)
|
||||
//{
|
||||
// for (TopicRecorder *tr : qAsConst(m_topicRecorder))
|
||||
// {
|
||||
// if (tr->isFailed()) // skip, plus already in m_failedExportTopicIds by exportFailed signal
|
||||
// continue;
|
||||
|
||||
// try
|
||||
// {
|
||||
// tr->saveRecording(tmpDirPath);
|
||||
// }
|
||||
// catch (const QString &msg)
|
||||
// {
|
||||
// ERROR_LOG(m_logger) << "Saving recording of topic '" << tr->topicName() << "', id: " << tr->topicId() << " failed! " << msg;
|
||||
// m_failedExportTopicIds += tr->topicId();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
void ScreenRecorder::setCurrentTime(qint64 currentTime)
|
||||
{
|
||||
emit newTime(currentTime);
|
||||
}
|
||||
|
||||
void ScreenRecorder::clear()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Clearing TopicRecorder.";
|
||||
|
||||
for (TopicRecorder *currentTopicRecorder : qAsConst(m_topicRecorder))
|
||||
{
|
||||
currentTopicRecorder->killAndClear();
|
||||
delete currentTopicRecorder;
|
||||
}
|
||||
|
||||
m_topicRecorder.clear();
|
||||
}
|
||||
|
||||
void ScreenRecorder::waitForFinishedRecording()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Waiting for TopicRecorder to finish ...";
|
||||
|
||||
for (TopicRecorder *currentTopicRecorder : qAsConst(m_topicRecorder))
|
||||
currentTopicRecorder->waitForFinishedRecording();
|
||||
|
||||
DEBUG_LOG(m_logger) << "... all TopicRecorder finished.";
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* class for recording topics on a screen
|
||||
* $Id:
|
||||
* $HeadURL:
|
||||
* $Author:
|
||||
* Created by Hannes Fritsche, SLTE, 2023-01-18
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTemporaryDir>
|
||||
#include <QSet>
|
||||
#include <Services/PresentationManagement/logger.h>
|
||||
#include "pre_topic_recorder.h"
|
||||
|
||||
|
||||
class QProcess;
|
||||
class Screen;
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class TopicItem;
|
||||
|
||||
class ScreenRecorder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public: // methods
|
||||
ScreenRecorder(QObject *parent = nullptr);
|
||||
~ScreenRecorder() { clear(); }
|
||||
|
||||
|
||||
|
||||
|
||||
void cut();
|
||||
void save(const QString &tmpDirPath);
|
||||
|
||||
|
||||
|
||||
|
||||
void addTopicsForScreen(Screen *screen, const std::vector<TopicItem> &topicItems);
|
||||
void setCurrentTime(qint64 currentTime);
|
||||
|
||||
void clear();
|
||||
void waitForFinishedRecording();
|
||||
void saveRecordings(const QString &tmpDirPath); // old
|
||||
const QSet<int> &failedExportTopicIds() const { return m_failedExportTopicIds; }
|
||||
|
||||
signals:
|
||||
void newTime(qint64 time);
|
||||
void topicExportFailed(const int failedTopicId);
|
||||
void cancel();
|
||||
|
||||
private: // attributes
|
||||
QVector<TopicRecorder *> m_topicRecorder;
|
||||
QSet<int> m_failedExportTopicIds;
|
||||
LOGGER_T m_logger;
|
||||
}; // class ScreenRecorder
|
||||
|
||||
} // namespace PresentationManagement
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* class for recording topics on a screen
|
||||
* $Id:
|
||||
* $HeadURL:
|
||||
* $Author:
|
||||
* Created by Hannes Fritsche, SLTE, 2023-01-18
|
||||
*/
|
||||
|
||||
#include "pre_screen_shotter.h"
|
||||
#include <QProcess>
|
||||
#include "pre_screen.h"
|
||||
#include <limits>
|
||||
#include "pre_topic_item.h"
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
ScreenShotter::ScreenShotter(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_logger(getLogger(QStringLiteral("ScreenShotter")))
|
||||
{}
|
||||
|
||||
void ScreenShotter::addTopicsForScreen(Screen *screen, const std::vector<TopicItem> &topicItems)
|
||||
{
|
||||
m_topicShotter.reserve(m_topicShotter.size() + topicItems.size());
|
||||
|
||||
for (const TopicItem ¤tTopicItem : topicItems)
|
||||
{
|
||||
const auto id = currentTopicItem.topicId();
|
||||
const qint64 startTime = currentTopicItem.startTime();
|
||||
const qint64 stopTime = currentTopicItem.startTime() + currentTopicItem.duration();
|
||||
const QString name(currentTopicItem.channel().replace(QStringLiteral(" "), QStringLiteral("_")) + QString::number(currentTopicItem.id()));
|
||||
const double topicX = currentTopicItem.topicData().x;
|
||||
const double topicY = currentTopicItem.topicData().y;
|
||||
const double topicW = currentTopicItem.topicData().w;
|
||||
const double topicH = currentTopicItem.topicData().h;
|
||||
ERROR_LOG(m_logger) << "ScreenShotter::addTopicsForScreen: x: " << topicX << ", y: " << topicY << ", w: " << topicW << ", h: " << topicH;
|
||||
TopicShotter *topicShotter = new TopicShotter(id, name, screen, 0, startTime, stopTime, topicX, topicY,
|
||||
topicW, topicH, (!currentTopicItem.parentChannel().isEmpty()),
|
||||
currentTopicItem.description(), this);
|
||||
|
||||
connect(this, &ScreenShotter::newTime, topicShotter, &TopicShotter::onNewTime);
|
||||
connect(topicShotter, &TopicShotter::exportFailed, this, [this, id]() { emit topicExportFailed(id); });
|
||||
connect(topicShotter, &TopicShotter::exportFailed, this, [this, name, id]()
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Export failed for topic '" << name << "', id: " << id;
|
||||
m_failedExportTopicIds += id;
|
||||
});
|
||||
|
||||
m_topicShotter.append(topicShotter);
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "Added " << topicItems.size() << " TopicShotter.";
|
||||
}
|
||||
|
||||
void ScreenShotter::saveRecordings(const QString &tmpDirPath)
|
||||
{
|
||||
for (TopicShotter *tr : qAsConst(m_topicShotter))
|
||||
{
|
||||
if (tr->isFailed())
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
tr->saveRecording(tmpDirPath);
|
||||
}
|
||||
catch (const QString &msg)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Saving screenshot of topic '" << tr->topicName() << "', id: " << tr->topicId() << " failed! " << msg;
|
||||
m_failedExportTopicIds += tr->topicId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenShotter::setCurrentTime(qint64 currentTime)
|
||||
{
|
||||
emit newTime(currentTime);
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* class for recording topics on a screen
|
||||
* $Id:
|
||||
* $HeadURL:
|
||||
* $Author:
|
||||
* Created by Hannes Fritsche, SLTE, 2023-01-18
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTemporaryDir>
|
||||
#include <QSet>
|
||||
#include <Services/PresentationManagement/logger.h>
|
||||
#include "pre_topic_shotter.h"
|
||||
|
||||
|
||||
class QProcess;
|
||||
class Screen;
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class TopicItem;
|
||||
|
||||
class ScreenShotter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public: // methods
|
||||
ScreenShotter(QObject *parent = nullptr);
|
||||
|
||||
void addTopicsForScreen(Screen *screen, const std::vector<TopicItem> &topicItems);
|
||||
void setCurrentTime(qint64 currentTime);
|
||||
|
||||
void saveRecordings(const QString &tmpDirPath);
|
||||
const QSet<int> &failedExportTopicIds() const { return m_failedExportTopicIds; }
|
||||
|
||||
signals:
|
||||
void newTime(qint64 time);
|
||||
void topicExportFailed(const int failedTopicId);
|
||||
|
||||
private: // attributes
|
||||
QVector<TopicShotter *> m_topicShotter;
|
||||
QSet<int> m_failedExportTopicIds;
|
||||
LOGGER_T m_logger;
|
||||
}; // class ScreenShotter
|
||||
|
||||
} // namespace PresentationManagement
|
||||
Executable
+210
@@ -0,0 +1,210 @@
|
||||
#include "pre_script_delegate.h"
|
||||
#include "pre_script_dialog_drag_handler.h"
|
||||
#include "pre_script_model.h"
|
||||
|
||||
#include <LtsTimeline/timeline_header_view.h>
|
||||
#include <LtsTimeline/timeline_view.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGuiApplication>
|
||||
#include <QPainter>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
QColor ScriptDelegate::s_blockedBackgroundColor = QGuiApplication::palette().color(QPalette::Disabled, QPalette::Base);
|
||||
|
||||
QColor ScriptDelegate::s_validDragColor = QRgb(0x00FF66);
|
||||
|
||||
QColor ScriptDelegate::s_invalidDragColor = QRgb(0xFF3333);
|
||||
|
||||
|
||||
ScriptDelegate::ScriptDelegate(LtsTimeline::TimelineView *parent)
|
||||
: LtsTimeline::ItemDelegate(parent)
|
||||
{
|
||||
m_model = static_cast<ScriptModel *>(m_view->timelineModel());
|
||||
Q_ASSERT(m_model);
|
||||
m_dragHandler = static_cast<ScriptDialogDragHandler *>(m_view->dragHandler());
|
||||
Q_ASSERT(m_dragHandler);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC
|
||||
|
||||
void ScriptDelegate::paintDragItems(QPainter *painter) const
|
||||
{
|
||||
if (!m_dragHandler->isActive() && !m_dragHandler->m_isDragEventFromTopicEditor)
|
||||
return;
|
||||
|
||||
if (m_dragHandler->m_isDragEventFromTopicEditor)
|
||||
{
|
||||
paintDragItemFromEditor(painter);
|
||||
return;
|
||||
}
|
||||
|
||||
paintDragItemsFromView(painter);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE
|
||||
|
||||
void ScriptDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
if (index.column() == LtsTimeline::Section::eActuator)
|
||||
{
|
||||
painter->save();
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
paintTrackButtons(painter, option, index);
|
||||
painter->restore();
|
||||
return;
|
||||
}
|
||||
|
||||
auto drawItem = [this, &option, &painter, &index](const TopicItem &topicItem)
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
opt.rect.setLeft(std::max(m_view->toPixel(topicItem.startTime()), option.rect.left()));
|
||||
opt.rect.setRight(std::min(m_view->toPixel(topicItem.startTime() + topicItem.duration()), option.rect.right()) - 1);
|
||||
opt.rect.setBottom(opt.rect.bottom() - 1);
|
||||
topicItem.paint(painter, opt, index, m_view->selectedItemIds().contains(topicItem.id()));
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
const TopicTrack &track = static_cast<const TopicTrack &>(m_view->timelineModel()->track(index.internalId()));
|
||||
painter->save();
|
||||
painter->setPen(Qt::black);
|
||||
painter->drawRect(option.rect);
|
||||
painter->setClipping(true);
|
||||
painter->setClipRect(option.rect);
|
||||
|
||||
if (m_model->isFilterTrack(track.id()))
|
||||
{
|
||||
try
|
||||
{
|
||||
const TopicTrack &parentTrack = static_cast<const TopicTrack &>(m_view->timelineModel()->track(track.parentId()));
|
||||
const LtsTimeline::Items &parentItems = parentTrack.items();
|
||||
Q_ASSERT(m_model->isMainTrack(parentTrack.id()));
|
||||
QRect blockedBackgroundRect = option.rect;
|
||||
blockedBackgroundRect.setLeft(blockedBackgroundRect.left() + 1);
|
||||
blockedBackgroundRect.setTop(blockedBackgroundRect.top() + 1);
|
||||
blockedBackgroundRect.setBottom(blockedBackgroundRect.bottom() - 1);
|
||||
painter->setPen(s_blockedBackgroundColor);
|
||||
painter->setBrush(QBrush(s_blockedBackgroundColor));
|
||||
for (const LtsTimeline::ItemPtr &itemPtr : parentItems)
|
||||
{
|
||||
blockedBackgroundRect.setRight(m_view->toPixel(itemPtr->startTime()) - 1);
|
||||
if (blockedBackgroundRect.width() > 0)
|
||||
painter->drawRect(blockedBackgroundRect);
|
||||
blockedBackgroundRect.setLeft(m_view->toPixel(itemPtr->startTime() + itemPtr->duration()) + 1);
|
||||
}
|
||||
blockedBackgroundRect.setRight(option.rect.right() - 1);
|
||||
if (blockedBackgroundRect.width() > 0)
|
||||
painter->drawRect(blockedBackgroundRect);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
qCritical() << "ItemDelegate::paint -> painting background failed";
|
||||
}
|
||||
}
|
||||
|
||||
painter->setPen(Qt::black);
|
||||
const LtsTimeline::Items &items = track.items();
|
||||
for (const LtsTimeline::ItemPtr &item : items)
|
||||
drawItem(static_cast<const TopicItem &>(*item.get()));
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
qCritical() << "ItemDelegate::paint -> CATCH";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScriptDelegate::paintDragItemsFromView(QPainter *painter) const
|
||||
{
|
||||
if (m_dragHandler->m_dragItemIds.isEmpty() || !m_dragHandler->m_canDropTopicTypes)
|
||||
return;
|
||||
|
||||
const QModelIndex index = m_view->indexAt(m_dragHandler->m_dragPos);
|
||||
if (!index.isValid() || index.column() == LtsTimeline::Section::eActuator)
|
||||
return;
|
||||
|
||||
painter->save();
|
||||
|
||||
const QRect rightSectionRect = QRect(
|
||||
QPoint(m_view->header()->sectionPosition(LtsTimeline::Section::eItems), 0), m_view->geometry().bottomRight()
|
||||
);
|
||||
painter->setClipRect(rightSectionRect);
|
||||
int dx = m_dragHandler->deltaX();
|
||||
int dy = m_dragHandler->deltaY();
|
||||
try
|
||||
{
|
||||
if (index == m_dragHandler->m_dragStartIndex)
|
||||
{
|
||||
if (m_dragHandler->canMoveItemsTo(m_dragHandler->m_dragPos))
|
||||
painter->setBrush(QBrush(s_validDragColor));
|
||||
else
|
||||
painter->setBrush(QBrush(s_invalidDragColor));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_dragHandler->canDragItemsTo(m_dragHandler->m_dragPos))
|
||||
painter->setBrush(QBrush(s_validDragColor));
|
||||
else
|
||||
painter->setBrush(QBrush(s_invalidDragColor));
|
||||
}
|
||||
for (const int &itemId : m_dragHandler->m_dragItemIds)
|
||||
{
|
||||
LtsTimeline::Item &item = m_view->timelineModel()->item(itemId);
|
||||
QModelIndex idx = m_view->timelineModel()->indexOfTrack(item.parentTrackId());
|
||||
QRect visualRect = m_view->visualRect(idx);
|
||||
int left = m_view->toPixel(item.startTime()) + dx;
|
||||
int right = m_view->toPixel(item.startTime() + item.duration()) + dx;
|
||||
int top = visualRect.top() + dy;
|
||||
int bottom = visualRect.bottom() + dy;
|
||||
if (m_view->selectedItemIds().contains(itemId))
|
||||
painter->setPen(QPen(LtsTimeline::Item::s_highlightColor, 5));
|
||||
painter->drawRect(QRect(left, top, right - left - 1, bottom - top));
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void ScriptDelegate::paintDragItemFromEditor(QPainter *painter) const
|
||||
{
|
||||
try
|
||||
{
|
||||
const QModelIndex index = m_view->indexAt(m_dragHandler->m_dragPos);
|
||||
if (!index.isValid())// || index.column() != LtsTimeline::Section::eItems)
|
||||
return;
|
||||
|
||||
ScriptModel *model = static_cast<ScriptModel *>(m_view->timelineModel());
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
if (!model->canDropTopicType(m_dragHandler->m_dragItem->topicType(), index.internalId()))
|
||||
return;
|
||||
|
||||
painter->save();
|
||||
|
||||
QCursor *cursor = QApplication::overrideCursor();
|
||||
if (cursor)
|
||||
painter->setBrush(QBrush(cursor->shape() == Qt::ForbiddenCursor ? s_invalidDragColor : s_validDragColor));
|
||||
else // extremely rare, but possible ...
|
||||
painter->setBrush(QBrush(m_view->cursor().shape() == Qt::ForbiddenCursor ? s_invalidDragColor : s_validDragColor));
|
||||
QRect rect = m_view->visualRect(index);
|
||||
painter->setClipRect(rect);
|
||||
rect.setLeft(m_dragHandler->m_dragPos.x() - m_dragHandler->m_dragItemOffset + m_dragHandler->deltaX());
|
||||
rect.setRight(rect.left() + m_dragHandler->dragItemFromTopicEditorWidth());
|
||||
rect.setBottom(rect.top() + m_dragHandler->deltaY() - 1);
|
||||
painter->drawRect(rect);
|
||||
|
||||
painter->restore();
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <LtsTimeline/item_delegate.h>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class ScriptDialogDragHandler;
|
||||
class ScriptModel;
|
||||
class ScriptTimelineDialog;
|
||||
|
||||
|
||||
class ScriptDelegate : public LtsTimeline::ItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QColor s_blockedBackgroundColor;
|
||||
static QColor s_validDragColor;
|
||||
static QColor s_invalidDragColor;
|
||||
|
||||
explicit ScriptDelegate(LtsTimeline::TimelineView *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief paintDragItems paints dragged items over the view
|
||||
* @param painter painter to draw all dragged items on the view
|
||||
*/
|
||||
void paintDragItems(QPainter *painter) const override;
|
||||
|
||||
private:
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
|
||||
void paintDragItemsFromView(QPainter *painter) const;
|
||||
void paintDragItemFromEditor(QPainter *painter) const;
|
||||
|
||||
private:
|
||||
ScriptModel *m_model { nullptr };
|
||||
ScriptDialogDragHandler *m_dragHandler { nullptr };
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+4833
File diff suppressed because it is too large.
Load diff
Executable
+3539
File diff suppressed because it is too large.
Load diff
+470
@@ -0,0 +1,470 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* PresentationManagement - Script Editor
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-03-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pre_screen.h"
|
||||
#include "pre_journal_types.h"
|
||||
#include "pre_utils.h"
|
||||
|
||||
#include <DDS/Core/core_dtc.h>
|
||||
#include <LtsPluginManager/lts_plugin_manager.h>
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsUtilsGuiDds/request_helper.h>
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
#include <PluginInterfaces/topic_replay_interface.h>
|
||||
#include <Plugins/PresentationManagement/Gui/Common/topic_assignment.h>
|
||||
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
|
||||
#include <Plugins/SituationReplay/srp_gui_plugin_interface.h>
|
||||
#include <Services/OperatorManagement/opm_dtc.h>
|
||||
#include <Services/ExerciseManagement/exm_dtc.h>
|
||||
#include <Services/PresentationManagement/pre_dtc.h>
|
||||
#include <Services/PresentationManagement/pre_ftc.h>
|
||||
#include <Services/SituationReplayManagement/srm_ftc.h>
|
||||
#include <TopicEditorBase/topic_editor_utils.h>
|
||||
#include <LtsWidgets/lts_overlay_loading.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QMap>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ScriptDialog;
|
||||
class ScriptPlayerDialog;
|
||||
}
|
||||
namespace LtsPluginManager { class LtsPluginManager; }
|
||||
namespace LtsSession { class LtsSession; }
|
||||
namespace LtsTimeline { class TimelineWidget; }
|
||||
namespace LtsUtils { class Promise; }
|
||||
namespace SituationReplayManagement { class ForeignDataInterface; }
|
||||
namespace TakeHomePackageGenerator { class ExportProcess; }
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class ScreenShotter;
|
||||
class ScreenRecorder;
|
||||
class ScriptModel;
|
||||
class StoryboardDialog;
|
||||
class TopicDesigner;
|
||||
class TopicItem;
|
||||
class CoolExporter;
|
||||
|
||||
namespace Interface { class Ftc; }
|
||||
|
||||
|
||||
//template<typename Type = void *>
|
||||
class Scope
|
||||
{
|
||||
public:
|
||||
explicit Scope(const std::function<void (void)> &ctor, const std::function<void (void)> &dtor)
|
||||
//: m_fn(fn)
|
||||
//: m_data(ctor())
|
||||
: m_dtor(dtor)
|
||||
{
|
||||
ctor();
|
||||
}
|
||||
|
||||
~Scope()
|
||||
{
|
||||
m_dtor();
|
||||
}
|
||||
|
||||
// Scope(const std::function<Type (void)> &ctor, const std::function<void (Type)> &dtor)
|
||||
// //: m_fn(fn)
|
||||
// : m_data({})
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
private:
|
||||
//Type m_data;
|
||||
std::function<void (void)> m_dtor;
|
||||
};
|
||||
|
||||
|
||||
class ScriptTimelineDialog : public LtsWidgets::LtsDialog
|
||||
{
|
||||
public:
|
||||
Q_OBJECT
|
||||
|
||||
friend class ScriptDelegate;
|
||||
|
||||
public:
|
||||
friend class CoolExporter;
|
||||
|
||||
enum class ProjectionWidget { Unknown = 0, Screen1, Screen2, };
|
||||
enum class SpecialTopicType { Normal = 0, Replay, Filter, };
|
||||
|
||||
static TopicEditorBase::TopicData topicDataFromContent(
|
||||
const PresentationManagement::PresentationContent &content, int topicId, TopicEditorBase::TopicType topicType
|
||||
);
|
||||
static bool isFilter(TopicEditorBase::TopicType topicType);
|
||||
static bool isUsingAvsWindow(TopicEditorBase::TopicType topicType);
|
||||
static QString toReadableTime(qint64 msecs);
|
||||
|
||||
explicit ScriptTimelineDialog(const QString &menuGroup, QWidget *parent = nullptr);
|
||||
~ScriptTimelineDialog();
|
||||
|
||||
bool init(
|
||||
LtsSession::LtsSession *session, const int scriptId, const std::shared_ptr<Interface::Dtc> &dtc,
|
||||
const std::shared_ptr<Interface::Ftc> &ftc,
|
||||
const std::shared_ptr<SituationReplayManagement::ForeignDataInterface> &srmForeign,
|
||||
const std::shared_ptr<Core::Interface::Dtc> &coreDtc, const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
|
||||
const std::shared_ptr<ExerciseManagement::Interface::Dtc> &exmDtc
|
||||
);
|
||||
|
||||
void initInternal();
|
||||
|
||||
LtsTimeline::TimelineWidget *timeline();
|
||||
ScriptModel *model() { return m_model; }
|
||||
|
||||
int scriptId() const { return m_scriptId; }
|
||||
|
||||
bool isScriptRunning() const { return m_isScriptRunning; }
|
||||
qint64 scriptEndTime() const;
|
||||
|
||||
LtsSession::LtsSession *essentialsViewSession() { return m_essentialsViewSession; }
|
||||
bool isEssentialsViewOpen() const;
|
||||
|
||||
bool import(const QVector<PresentationContent> &contents);
|
||||
void load();
|
||||
|
||||
TopicEditorBase::TopicEditor *getTopicEditor(const TopicAssignment &topicAssignment);
|
||||
TopicEditorBase::TopicEditor *getTopicEditor(const TopicEditorBase::TopicData &data);
|
||||
|
||||
bool topicStringToType(const QString &typeName, TopicEditorBase::TopicType &topicType) const;
|
||||
|
||||
std::shared_ptr<SituationReplay::ForeignGuiInterface> getReplayGuiIfc() const
|
||||
{
|
||||
return m_essentialsViewsSrpIfc;
|
||||
}
|
||||
|
||||
private:
|
||||
// LtsDialog override
|
||||
void retranslate() override;
|
||||
bool apply() override;
|
||||
void aboutToClose() override;
|
||||
|
||||
void getReplaySession() noexcept(false);
|
||||
bool getTopics(QStringList &resultList);
|
||||
bool getTopic(
|
||||
TopicEditorBase::TopicType topicType, const std::shared_ptr<TopicEditorBase::Interface> &topicEditorIfc,
|
||||
const std::shared_ptr<TopicReplay::Factory> &topicReplayFactory,
|
||||
const std::shared_ptr<TopicReplay::Factory> &topicExportFactory, QStringList &resultList,
|
||||
const SpecialTopicType &specialTopicType = SpecialTopicType::Normal
|
||||
);
|
||||
bool getTopicItems();
|
||||
void getSitMapApp() noexcept(false);
|
||||
void getPresentationContent(); // tryAddTopics has to be called first to populate m_topicAssignments
|
||||
void getProjectionWidget(Screen **pointerToWidgetPointer, bool placeAutomatically = false);
|
||||
|
||||
// dds stuff
|
||||
bool enableFireEventFlash(bool enable);
|
||||
|
||||
// io stuff
|
||||
void exportPresentationContent(const Core::FunctionReply &baseReply);
|
||||
|
||||
// ui stuff
|
||||
void disableSessionDialog(LtsSession::LtsSession *session, bool disable);
|
||||
void startEssentialsViewStage2(const Core::ReplayPartition &replay, bool openTopicEditor, int topicType, int topicItemId);
|
||||
static bool hasEssentialsViewTopicType(TopicEditorBase::TopicType topicType);
|
||||
void previewSecond(qint64 time, int ignoreTopicForReplay = -1);
|
||||
TopicEditorBase::TopicEditor *createTopicEditor(const TopicEditorBase::TopicData &data);
|
||||
Screen *getProjectionWidget(const ProjectionWidget &projectionWidget);
|
||||
ProjectionWidget identifyProjectionWidget(const TopicItem &topicItem) const;
|
||||
static void deleteProjectionWidget(Screen **pointerToWidgetPointer);
|
||||
void addReplayWidget(int topicItemId, QWidget *replayWidget);
|
||||
void setReplayWidgetPosition(int topicItemId);
|
||||
void setReplayWidgetFreestanding(int topicItemId, bool freestanding);
|
||||
void displayPlaylist();
|
||||
bool isScreenFreestanding(const ProjectionWidget &projectionWidget) const;
|
||||
void setProjectionWidgetFreestanding(bool freestanding, const ProjectionWidget &projectionWidget);
|
||||
void avsVisibilityHack(QWidget *parentForScreen, bool visible);
|
||||
void avsFreestandingHack(bool freestanding, QWidget *projectionWidget, QWidget **parentForScreen);
|
||||
void enaDisa();
|
||||
void startEssentialsView(int topicItemId = -1, int topicType = -1, bool openTopicEditor = false);
|
||||
|
||||
// topic stuff
|
||||
void openContextMenu(QPoint pos, int topicItemId);
|
||||
void setTopicDescription(int topicItemId);
|
||||
void enableTopicEditors(bool enable);
|
||||
|
||||
// replay stuff
|
||||
qint64 positionTime() const;
|
||||
void setPositionTime(qint64 time, bool setByUser = false);
|
||||
void setPositionTimeActive(qint64 time, bool setByUser = false); // setPositionTime, but, if playing, also ticks and resets timeout
|
||||
void playTopic(int topicItemId);
|
||||
void pause();
|
||||
void stop(bool ended = false, bool ignoreFireEventFlashing = false);
|
||||
qint64 previousTopicTime() const;
|
||||
qint64 nextTopicTime() const;
|
||||
bool hasPreviousTopic() const { return previousTopicTime() > -1; }
|
||||
bool hasNextTopic() const { return nextTopicTime() > -1; }
|
||||
void removeTopicItemFromPlaylist(int topicItemId); // WARNING: use a mutable iterator while iterating over m_currentlyPlayingTopics and remove the entry before calling this method!
|
||||
|
||||
bool isTopicAlreadyOpened(const TopicEditorBase::TopicData &data);
|
||||
template <typename TopicEditorInterface>
|
||||
std::shared_ptr<TopicEditorBase::Interface> getTopicEditorIfc(TopicEditorBase::TopicType topicType)
|
||||
{
|
||||
std::shared_ptr<TopicEditorBase::Interface> topicEditorIfc;
|
||||
|
||||
if (!m_exerciseSession->interfaceFromGui<TopicEditorInterface>(topicEditorIfc))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "getTopicEditorIfc failed: Cannot add topic type "
|
||||
<< TopicEditorBase::topicTypeString(topicType)
|
||||
<< " because topic editor ifc is missing.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return topicEditorIfc;
|
||||
}
|
||||
|
||||
template <typename TopicReplayInterface>
|
||||
std::shared_ptr<TopicReplay::Factory> getTopicReplayFactory(TopicEditorBase::TopicType topicType)
|
||||
{
|
||||
std::shared_ptr<TopicReplay::Factory> topicReplayFactory;
|
||||
|
||||
if (!m_replaySession->interfaceFromGui<TopicReplayInterface>(topicReplayFactory))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "getTopicIfcFactory failed: Cannot add topic type "
|
||||
<< TopicEditorBase::topicTypeString(topicType)
|
||||
<< " because topic ifc factory is missing.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return topicReplayFactory;
|
||||
}
|
||||
|
||||
bool isTopicDisabled(TopicEditorBase::TopicType topicType) const;
|
||||
|
||||
template <typename TopicEditorGuiInterface,
|
||||
typename TopicReplayFactory = TopicReplay::Factory,
|
||||
typename TopicExportFactory = TopicReplayFactory>
|
||||
bool tryAddTopic(TopicEditorBase::TopicType topicType,
|
||||
QStringList &resultList,
|
||||
const SpecialTopicType &specialTopicType = SpecialTopicType::Normal)
|
||||
{
|
||||
// qWarning() << "mep" << QStringLiteral("%1:%2").arg(QString::fromUtf8(__FILE__).split(QStringLiteral("/")).last()).arg(__LINE__);
|
||||
|
||||
if (isTopicDisabled(topicType))
|
||||
return true;
|
||||
|
||||
std::shared_ptr<TopicEditorBase::Interface> topicEditorIfc;
|
||||
std::shared_ptr<TopicReplay::Factory> topicReplayFactory;
|
||||
std::shared_ptr<TopicReplay::Factory> topicExportFactory;
|
||||
|
||||
if (!std::is_same<TopicEditorBase::Interface, TopicEditorGuiInterface>::value)
|
||||
{
|
||||
topicEditorIfc = getTopicEditorIfc<TopicEditorGuiInterface>(topicType);
|
||||
|
||||
if (topicEditorIfc == nullptr)
|
||||
{
|
||||
resultList.append(TopicEditorBase::topicTypeString(topicType));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!std::is_same<TopicReplay::Factory, TopicReplayFactory>::value)
|
||||
{
|
||||
topicReplayFactory = getTopicReplayFactory<TopicReplayFactory>(topicType);
|
||||
|
||||
if (topicReplayFactory == nullptr)
|
||||
{
|
||||
resultList.append(TopicEditorBase::topicTypeString(topicType));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!std::is_same<TopicReplay::Factory, TopicExportFactory>::value)
|
||||
{
|
||||
topicExportFactory = getTopicReplayFactory<TopicExportFactory>(topicType);
|
||||
|
||||
if (topicExportFactory == nullptr)
|
||||
{
|
||||
resultList.append(TopicEditorBase::topicTypeString(topicType));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
topicExportFactory = topicReplayFactory;
|
||||
|
||||
return getTopic(topicType, topicEditorIfc, topicReplayFactory, topicExportFactory, resultList, specialTopicType);
|
||||
}
|
||||
|
||||
bool hasPlacedBothProjectionWidgets() const;
|
||||
|
||||
private slots:
|
||||
void onChangesMade();
|
||||
void onChangesWereMade();
|
||||
void onDiscard();
|
||||
void onNull(QObject *obj);
|
||||
|
||||
// io stuff
|
||||
void onLoad();
|
||||
void onExport();
|
||||
void onExport2();
|
||||
void onExportSuccessful(const QString &path, const QString &failedTopics);
|
||||
void onBurnDvD(const QString &strExportPath);
|
||||
void onBurningFinished(bool success);
|
||||
|
||||
// ui stuff
|
||||
void onPresentationContent(const GetPresentationContentReply &reply);
|
||||
void onEssentialsView();
|
||||
void onTopicItemPositionChanged(QWidget *previewWidget, int ignoreTopicForReplay);
|
||||
void onCheckTopicItemDuration(int duration, LtsUtils::Promise *promise);
|
||||
void onTopicItemModified();
|
||||
void onTopicItemRemoved(int topicItemId);
|
||||
void onTopicItemSelectionChanged(const QVector<int> &selectedItemIds);
|
||||
void onUpdateTopicEditor(int topicItemId);
|
||||
void onTopicDesignerChangesMade();
|
||||
void onTopicEditorChangesSaved(const TopicEditorBase::TopicData &data);
|
||||
|
||||
// topic stuff
|
||||
void onGetTopicEditor(const TopicEditorBase::TopicData &data);
|
||||
void onEditSelectedTopicItem();
|
||||
void onEditTopicItem(int itemId);
|
||||
void onRemoveSelectedTopics();
|
||||
void onContextMenu(QPoint pos);
|
||||
// void onMergeTopics();
|
||||
// void onSplitTopic();
|
||||
|
||||
// replay stuff
|
||||
void onTimeout();
|
||||
void onPlay();
|
||||
void onPause();
|
||||
void onStop();
|
||||
void onNextTopic();
|
||||
void onPreviousTopic();
|
||||
void onPlayUntilNextTopic();
|
||||
|
||||
void onShowStoryboard();
|
||||
void onShowJournal();
|
||||
|
||||
// other stuff
|
||||
bool checkBuildingViewerAndEssentialsView();
|
||||
|
||||
signals:
|
||||
void scriptChanged(int);
|
||||
void isPlaying(bool);
|
||||
void stopped(bool ended);
|
||||
void scriptTick(qint64 positionTime);
|
||||
|
||||
private:
|
||||
LtsSession::LtsSession *m_exerciseSession { nullptr }; // aka. script editor app
|
||||
LtsSession::LtsSession *m_replaySession { nullptr };
|
||||
LtsSession::LtsSession *m_sitMapSession { nullptr };
|
||||
LtsSession::LtsSession *m_essentialsViewSession { nullptr };
|
||||
LtsPluginManager::LtsPluginManager *m_essentialsViewApp { nullptr };
|
||||
LtsPluginManager::LtsPluginManager *m_sitMapApp { nullptr };
|
||||
LtsUtils::RequestHelper *m_requestHelper { nullptr };
|
||||
TakeHomePackageGenerator::ExportProcess *m_exportProcess { nullptr }; // OLD exporter!
|
||||
|
||||
std::shared_ptr<Core::Interface::Dtc> m_coreDtc { nullptr };
|
||||
std::shared_ptr<PresentationManagement::Interface::Dtc> m_dtc { nullptr };
|
||||
std::shared_ptr<PresentationManagement::Interface::Ftc> m_ftc { nullptr };
|
||||
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc { nullptr };
|
||||
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc { nullptr };
|
||||
|
||||
|
||||
std::shared_ptr<SituationReplay::ForeignGuiInterface> m_essentialsViewsSrpIfc;
|
||||
std::shared_ptr<SituationMap::ForeignGuiInterface> m_essentialsViewsSitMapIfc;
|
||||
|
||||
std::shared_ptr<SituationReplayManagement::ForeignDataInterface> m_srmForeignDataIfc { nullptr };
|
||||
|
||||
TopicEditorBase::TopicEditor *m_currentTopicEditor { nullptr };
|
||||
QPointer<TopicDesigner> m_currentTopicDesigner;
|
||||
QPointer<StoryboardDialog> m_storyboardDialog;
|
||||
QWidget *m_previousParentForScreen1 { nullptr };
|
||||
QWidget *m_previousParentForScreen2 { nullptr };
|
||||
Screen *m_projectionWidget1 { nullptr };
|
||||
Screen *m_projectionWidget2 { nullptr };
|
||||
|
||||
QString m_menuGroup;
|
||||
QString m_exerciseName;
|
||||
int m_scriptId { 0 };
|
||||
int m_scriptsExerciseId { 0 };
|
||||
QString m_scriptName;
|
||||
QString m_scriptOwner;
|
||||
qint64 m_lastModified { 0 };
|
||||
|
||||
bool m_ignoreChanges { false };
|
||||
bool m_isApplyEnabled { true };
|
||||
bool m_isContextMenuOpen { false };
|
||||
|
||||
int m_selectedTeId;
|
||||
QMap<QString, TopicAssignment> m_topicAssignments;
|
||||
QMap<TopicEditorBase::TopicType, TopicAssignment> m_trackEntryToTopicAssignment;
|
||||
QMultiMap<int, LtsScriptTimeline::TrackEntry *> m_topicMap; // <seconds, trackEntry>
|
||||
|
||||
QTimer *m_presentationTimer { nullptr };
|
||||
qint64 m_firstTopicTime { -1 };
|
||||
qint64 m_lastTopicTime { -1 };
|
||||
qint64 m_nextPauseTime { -1 };
|
||||
int m_duration { 0 };
|
||||
int m_ignoreTopicForReplay { -1 };
|
||||
bool m_isFirstPlayAfterStop { true };
|
||||
bool m_isScriptRunning { false };
|
||||
bool m_doNotAllowPrevNext { false };
|
||||
bool m_isPlayTopicMethodStillRunning { false };
|
||||
bool m_stopAfterPlayingFilterTopic { false };
|
||||
|
||||
MainJournal m_journal;
|
||||
|
||||
bool m_isTopicsScreen1Freestanding { false };
|
||||
bool m_isTopicsScreen2Freestanding { false };
|
||||
bool m_isAvsVisibilityHackActive { false };
|
||||
bool m_isPaused { false };
|
||||
|
||||
ScriptModel *m_model { nullptr };
|
||||
|
||||
QVector<int> m_playlistTopicItemIds;
|
||||
QVector<int> m_mainTopicItemIds;
|
||||
QVector<int> m_filterTopicItemIds;
|
||||
|
||||
Ui::ScriptDialog *m_ui { nullptr };
|
||||
bool m_lastScriptHasEnded { false };
|
||||
NGL::INGLLogger &m_logger;
|
||||
QMap<TopicEditorBase::TopicType, int> m_avaibleTopics{}; // <data.topicType, data.id>
|
||||
QVector<TopicEditorBase::TopicType> m_modifiedTopicTypesList;
|
||||
|
||||
LtsSession::LtsSession *m_session { nullptr };
|
||||
};
|
||||
|
||||
|
||||
class CoolExporter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CoolExporter(ScriptTimelineDialog &dialog);
|
||||
QSet<int> go() noexcept(false);
|
||||
|
||||
signals:
|
||||
void scriptTick(qint64 positionTime);
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
void cancelationPoint() noexcept(false);
|
||||
|
||||
ScriptTimelineDialog &m_this;
|
||||
std::unique_ptr<LtsWidgets::LtsOverlayLoading> m_loading;
|
||||
bool m_stoppedPrematurely { false };
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScriptDialog</class>
|
||||
<widget class="QWidget" name="ScriptDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1010</width>
|
||||
<height>215</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Script Editor</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="LtsTimeline::TimelineWidget" name="timeline" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbReplayControls">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Replay Controls</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QWidget" name="wReplayControls" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,999">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hboxReplayControls">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vboxReplayControlsMain">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hboxReplayControlsScreens">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbInitScreen1">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set Screen 1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbInitScreen2">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set Screen 2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbStop">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stop</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbPause">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Pause</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbPlay">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Play</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vboxReplayControlsJump">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbUntilNextTopic">
|
||||
<property name="text">
|
||||
<string>Play Until Next Topic</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbPreviousTopic">
|
||||
<property name="text">
|
||||
<string>Previous Topic</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbNextTopic">
|
||||
<property name="text">
|
||||
<string>Next Topic</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formReplayInfo">
|
||||
<property name="horizontalSpacing">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="durationLabel">
|
||||
<property name="text">
|
||||
<string>Presentation duration:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="lblDuration">
|
||||
<property name="text">
|
||||
<string notr="true"> - </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbxPos">
|
||||
<property name="text">
|
||||
<string>Pos:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSlider" name="sPos">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="const" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="lbxCurrentPosition">
|
||||
<property name="text">
|
||||
<string>Current position:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="hboxCurrentPosition">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblPos">
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lbxSlash">
|
||||
<property name="text">
|
||||
<string>/</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblMax">
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="vboxStatus">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbxStatus">
|
||||
<property name="text">
|
||||
<string>Status:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="lwStatus">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Free</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LtsTimeline::TimelineWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>LtsTimeline/timeline_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
+495
@@ -0,0 +1,495 @@
|
||||
#include "pre_script_dialog_drag_handler.h"
|
||||
#include "pre_script_model.h"
|
||||
|
||||
#include <LtsTimeline/item_delegate.h>
|
||||
#include <LtsTimeline/timeline_widget.h>
|
||||
#include <TopicEditorBase/topic_editor_utils.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDrag>
|
||||
#include <QMimeData>
|
||||
#include <QMouseEvent>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
ScriptDialogDragHandler::ScriptDialogDragHandler(TimelineView *view)
|
||||
: TimelineViewDragHandler(view)
|
||||
, m_timer(new QTimer(this))
|
||||
{
|
||||
m_model = qobject_cast<ScriptModel *>(m_view->timelineModel());
|
||||
Q_ASSERT(m_model);
|
||||
|
||||
m_timer->setSingleShot(true);
|
||||
connect(m_timer, &QTimer::timeout, this, [this]()
|
||||
{
|
||||
m_mutex = false;
|
||||
});
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC
|
||||
|
||||
void ScriptDialogDragHandler::dragEnterEvent(QDragEnterEvent *e)
|
||||
{
|
||||
if (!m_model->isEditable())
|
||||
{
|
||||
e->setAccepted(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->source() == m_view)
|
||||
{
|
||||
TimelineViewDragHandler::dragEnterEvent(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e->mimeData()->hasFormat(TopicEditorBase::TopicData::mime))
|
||||
{
|
||||
e->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream dataStream(e->mimeData()->data(TopicEditorBase::TopicData::mime));
|
||||
dataStream >> m_dragTopicData;
|
||||
m_dragItem = std::make_unique<TopicItem>(TopicItem(m_dragTopicData));
|
||||
m_isDragEventFromTopicEditor = true;
|
||||
m_dragPos = e->pos();
|
||||
if (m_dragItem->duration() > m_view->zoomFactor())
|
||||
{
|
||||
ZoomFactor zoom = Zoom::ceil(m_dragItem->duration());
|
||||
if (m_dragItem->duration() > m_view->duration())
|
||||
m_view->setDuration(zoom);
|
||||
m_view->setZoomFactor(zoom);
|
||||
}
|
||||
e->acceptProposedAction();
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::dragMoveEvent(QDragMoveEvent *e)
|
||||
{
|
||||
if (!m_isDragEventFromTopicEditor)
|
||||
{
|
||||
TimelineViewDragHandler::dragMoveEvent(e);
|
||||
setCanDropTopicTypes();
|
||||
try
|
||||
{
|
||||
adjust();
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION <<"zoom and scroll failed:" << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION <<"zoom and scroll failed:"; }
|
||||
return;
|
||||
}
|
||||
|
||||
m_dragPos = e->pos();
|
||||
setCanDropTopicTypes();
|
||||
m_view->header()->setHoverPosition(m_dragPos.x());
|
||||
e->setAccepted(canDropTopicItemAt(e->pos()));
|
||||
|
||||
try
|
||||
{
|
||||
adjust();
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION <<"zoom and scroll failed:" << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION <<"zoom and scroll failed:"; }
|
||||
|
||||
m_view->viewport()->update();
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::dragLeaveEvent(QDragLeaveEvent *e)
|
||||
{
|
||||
if (!m_isDragEventFromTopicEditor)
|
||||
{
|
||||
TimelineViewDragHandler::dragLeaveEvent(e);
|
||||
m_view->setEndTime(std::max(ScriptModel::s_minScriptDuration, m_model->lastTopicEnd() + ScriptModel::s_scriptEndOffset));
|
||||
return;
|
||||
}
|
||||
|
||||
qint64 end = -1;
|
||||
try
|
||||
{
|
||||
end = maxDragItemEnd();
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "obtaining end time failed:" << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION << "obtaining end time failed:"; }
|
||||
m_view->setEndTime(std::max(ScriptModel::s_minScriptDuration, m_model->lastTopicEnd() + ScriptModel::s_scriptEndOffset));
|
||||
if (end != -1 && m_view->currentTime() + m_view->zoomFactor() < end)
|
||||
m_view->setCurrentTime(end + scrollOffset() - m_view->zoomFactor());
|
||||
|
||||
m_dragPos = {};
|
||||
m_isDragEventFromTopicEditor = false;
|
||||
m_dragTopicData = TopicEditorBase::TopicData();
|
||||
m_dragItemOffset = 0;
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::dropEvent(QDropEvent *e)
|
||||
{
|
||||
if (!m_isDragEventFromTopicEditor)
|
||||
{
|
||||
const QVector<int> dragItemIdsCopy = m_dragItemIds;
|
||||
TimelineViewDragHandler::dropEvent(e);
|
||||
for (int dragItemId : dragItemIdsCopy)
|
||||
{
|
||||
try
|
||||
{
|
||||
TopicItem &topicItem = m_model->topicItem(dragItemId);
|
||||
const TopicTrack &track = static_cast<const TopicTrack &>(m_model->track(topicItem.parentTrackId()));
|
||||
topicItem.topicData().startTime = topicItem.startTime() / 1000LL;
|
||||
topicItem.setChannel(track.internalName());
|
||||
if (track.parentId() != m_model->rootTrack().id())
|
||||
topicItem.setParentChannel(static_cast<const TopicTrack &>(m_model->track(track.parentId())).internalName());
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "failed for item with id =" << dragItemId << ": " << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION << "failed for item with id =" << dragItemId; }
|
||||
}
|
||||
m_view->setEndTime(std::max(ScriptModel::s_minScriptDuration, m_model->lastTopicEnd() + ScriptModel::s_scriptEndOffset));
|
||||
return;
|
||||
}
|
||||
|
||||
if (canDropTopicItemAt(e->pos()))
|
||||
{
|
||||
try
|
||||
{
|
||||
const QModelIndex index = m_view->indexAt(e->pos());
|
||||
m_dragTopicData.startTime = m_view->toTime(m_dragPos.x() - m_dragItemOffset) / 1000;
|
||||
m_model->insertTopicItem(index.internalId(), std::move(TopicItem(m_dragTopicData)));
|
||||
emit m_view->timelineModel()->dataChanged(index, index);
|
||||
e->accept();
|
||||
}
|
||||
catch (const QString &errMsg)
|
||||
{
|
||||
qCritical() << errMsg;
|
||||
e->ignore();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
qDebug() << "drop of item failed";
|
||||
e->ignore();
|
||||
}
|
||||
}
|
||||
else
|
||||
e->ignore();
|
||||
|
||||
qint64 end = -1;
|
||||
try
|
||||
{
|
||||
end = maxDragItemEnd();
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "obtaining end time failed:" << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION << "obtaining end time failed:"; }
|
||||
m_view->setEndTime(std::max(ScriptModel::s_minScriptDuration, m_model->lastTopicEnd() + ScriptModel::s_scriptEndOffset));
|
||||
if (end != -1 && m_view->currentTime() + m_view->zoomFactor() < end)
|
||||
m_view->setCurrentTime(end + scrollOffset() - m_view->zoomFactor());
|
||||
|
||||
m_dragPos = {};
|
||||
m_isDragEventFromTopicEditor = false;
|
||||
m_dragTopicData = TopicEditorBase::TopicData();
|
||||
m_dragItemOffset = 0;
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::mousePressEvent(QMouseEvent *e)
|
||||
{
|
||||
TimelineViewDragHandler::mousePressEvent(e);
|
||||
addFilterItemsToDragItems();
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
TimelineViewDragHandler::mouseMoveEvent(e);
|
||||
|
||||
try
|
||||
{
|
||||
if (e->buttons() == Qt::LeftButton)
|
||||
adjust();
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "zoom and scroll failed:" << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION <<"zoom and scroll failed:"; }
|
||||
|
||||
try
|
||||
{
|
||||
QModelIndex index = m_view->indexAt(e->pos());
|
||||
qint64 time = m_view->toTime(e->pos().x());
|
||||
const Track &track = m_model->track(index.internalId());
|
||||
TopicItem &topicItem = m_model->topicItem(track.itemAt(time).id());
|
||||
qint64 replayTime = topicItem.replayTime(time);
|
||||
if (replayTime != -1)
|
||||
{
|
||||
m_view->setCursorTextVisible(true);
|
||||
m_view->setCursorText(DateTime::toString(replayTime, DateTimeFormat::DTG));
|
||||
}
|
||||
else
|
||||
m_view->setCursorTextVisible(false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
m_view->setCursorTextVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
if (m_timer->isActive())
|
||||
m_timer->stop();
|
||||
m_mutex = false;
|
||||
|
||||
qint64 end = -1;
|
||||
try
|
||||
{
|
||||
end = maxDragItemEnd();
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "obtaining end time failed:" << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION << "obtaining end time failed:"; }
|
||||
|
||||
const QVector<int> dragIds = m_dragItemIds;
|
||||
TimelineViewDragHandler::mouseReleaseEvent(e);
|
||||
for (int dragId : dragIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
TopicItem &topicItem = m_model->topicItem(dragId);
|
||||
topicItem.topicData().startTime = topicItem.startTime() / 1000LL;
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "adjusting topic data failed:" << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION << "adjusting topic data failed"; }
|
||||
}
|
||||
|
||||
m_view->setEndTime(std::max(ScriptModel::s_minScriptDuration, m_model->lastTopicEnd() + ScriptModel::s_scriptEndOffset));
|
||||
if (end != -1 && m_view->currentTime() + m_view->zoomFactor() < end)
|
||||
m_view->setCurrentTime(end + scrollOffset() - m_view->zoomFactor());
|
||||
}
|
||||
|
||||
|
||||
int ScriptDialogDragHandler::deltaX() const
|
||||
{
|
||||
if (!m_isDragEventFromTopicEditor)
|
||||
return TimelineViewDragHandler::deltaX();
|
||||
|
||||
int xCursor = m_dragPos.x();
|
||||
qint64 t = m_view->toTime(xCursor);
|
||||
t -= (t % m_view->timelineModel()->snapStep());
|
||||
int x = m_view->toPixel(t);
|
||||
return x - xCursor;
|
||||
}
|
||||
|
||||
int ScriptDialogDragHandler::deltaY() const
|
||||
{
|
||||
Q_ASSERT(m_view->itemDelegate());
|
||||
return m_isDragEventFromTopicEditor ? m_view->itemDelegate()->sizeHint().height() : TimelineViewDragHandler::deltaY();
|
||||
}
|
||||
|
||||
|
||||
int ScriptDialogDragHandler::dragItemFromTopicEditorWidth() const
|
||||
{
|
||||
int xStart = m_view->toPixel(static_cast<qint64>(m_dragTopicData.startTime) * 1000);
|
||||
int xEnd = m_view->toPixel(static_cast<qint64>(m_dragTopicData.startTime + m_dragTopicData.duration) * 1000);
|
||||
return xEnd - xStart;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE
|
||||
|
||||
void ScriptDialogDragHandler::moveItemsLeft(const QVector<int> &, int dt)
|
||||
{
|
||||
m_dragItemIds = m_view->selectedItemIds();
|
||||
addFilterItemsToDragItems();
|
||||
TimelineViewDragHandler::moveItemsLeft(m_dragItemIds, dt);
|
||||
try
|
||||
{
|
||||
auto iter = std::min_element(m_dragItemIds.begin(), m_dragItemIds.end(), [this](int leftItemId, int rightItemId)
|
||||
{
|
||||
const Item &leftItem = m_model->item(leftItemId);
|
||||
const Item &rightItem = m_model->item(rightItemId);
|
||||
return leftItem.startTime() + leftItem.duration() < rightItem.startTime() + rightItem.duration();
|
||||
});
|
||||
const Item &startItem = m_model->item(*iter);
|
||||
adjustLeft(std::max(m_view->startTime(), startItem.startTime() - scrollOffset()));
|
||||
}
|
||||
catch (...) {}
|
||||
m_dragItemIds.clear();
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::moveItemsRight(const QVector<int> &, int dt)
|
||||
{
|
||||
m_dragItemIds = m_view->selectedItemIds();
|
||||
addFilterItemsToDragItems();
|
||||
TimelineViewDragHandler::moveItemsRight(m_dragItemIds, dt);
|
||||
try
|
||||
{
|
||||
auto iter = std::max_element(m_dragItemIds.begin(), m_dragItemIds.end(), [this](int leftItemId, int rightItemId)
|
||||
{
|
||||
const Item &leftItem = m_model->item(leftItemId);
|
||||
const Item &rightItem = m_model->item(rightItemId);
|
||||
return leftItem.startTime() + leftItem.duration() < rightItem.startTime() + rightItem.duration();
|
||||
});
|
||||
const Item &endItem = m_model->item(*iter);
|
||||
adjustRight(endItem.startTime() + endItem.duration() + scrollOffset());
|
||||
}
|
||||
catch (...) {}
|
||||
m_dragItemIds.clear();
|
||||
}
|
||||
|
||||
|
||||
int ScriptDialogDragHandler::scrollOffset() const
|
||||
{
|
||||
return static_cast<qint64>(m_view->zoomFactor()) * 5 / 100;
|
||||
}
|
||||
|
||||
qint64 ScriptDialogDragHandler::minDragItemStart() const noexcept(false)
|
||||
{
|
||||
qint64 start = m_view->endTime();
|
||||
if (!m_dragItemIds.isEmpty())
|
||||
{
|
||||
auto iter = std::min_element(m_dragItemIds.begin(), m_dragItemIds.end(), [this](int leftItemId, int rightItemId)
|
||||
{
|
||||
const Item &leftItem = m_model->item(leftItemId);
|
||||
const Item &rightItem = m_model->item(rightItemId);
|
||||
return leftItem.startTime() + leftItem.duration() < rightItem.startTime() + rightItem.duration();
|
||||
});
|
||||
const Item &startItem = m_model->item(*iter);
|
||||
start = std::max(m_view->startTime(), startItem.startTime() + deltaTime());
|
||||
}
|
||||
else if (m_isDragEventFromTopicEditor)
|
||||
start = m_view->toTime(m_dragPos.x());
|
||||
return start;
|
||||
}
|
||||
|
||||
qint64 ScriptDialogDragHandler::maxDragItemEnd() const noexcept(false)
|
||||
{
|
||||
qint64 end = m_view->startTime();
|
||||
if (!m_dragItemIds.isEmpty())
|
||||
{
|
||||
auto iter = std::max_element(m_dragItemIds.begin(), m_dragItemIds.end(), [this](int leftItemId, int rightItemId)
|
||||
{
|
||||
const Item &leftItem = m_model->item(leftItemId);
|
||||
const Item &rightItem = m_model->item(rightItemId);
|
||||
return leftItem.startTime() + leftItem.duration() < rightItem.startTime() + rightItem.duration();
|
||||
});
|
||||
const Item &endItem = m_model->item(*iter);
|
||||
end = endItem.startTime() + endItem.duration() + deltaTime();
|
||||
}
|
||||
else if (m_isDragEventFromTopicEditor)
|
||||
end = m_view->toTime(m_dragPos.x()) + static_cast<qint64>(m_dragTopicData.duration) * 1000LL;
|
||||
return end;
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::adjust() noexcept(false)
|
||||
{
|
||||
qint64 start = std::max(m_view->startTime(), minDragItemStart() - scrollOffset());
|
||||
qint64 end = maxDragItemEnd() + scrollOffset();
|
||||
if (end > m_view->currentTime() + m_view->zoomFactor())
|
||||
{
|
||||
qDebug() << "adjusting right";
|
||||
adjustRight(end);
|
||||
}
|
||||
else if (start < m_view->currentTime())
|
||||
{
|
||||
qDebug() << "adjusting left";
|
||||
adjustLeft(start);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::adjustLeft(qint64 start) noexcept(false)
|
||||
{
|
||||
if (start >= m_view->currentTime())
|
||||
return;
|
||||
|
||||
ZoomFactor newZoom = Zoom::increment(m_view->zoomFactor());
|
||||
if (m_view->zoomFactor() < newZoom && newZoom <= m_view->upperZoomFactor())
|
||||
{
|
||||
m_view->setCurrentTime(m_view->currentTime() - (newZoom - m_view->zoomFactor()));
|
||||
m_view->setZoomFactor(newZoom);
|
||||
return;
|
||||
}
|
||||
|
||||
m_view->setCurrentTime(m_view->currentTime() - scrollOffset());
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::adjustRight(qint64 end) noexcept(false)
|
||||
{
|
||||
if (end <= m_view->currentTime() + m_view->zoomFactor())
|
||||
return;
|
||||
|
||||
ZoomFactor newZoom = Zoom::increment(m_view->zoomFactor());
|
||||
if (m_view->zoomFactor() < newZoom && newZoom <= m_view->maximumZoomFactor())
|
||||
{
|
||||
if (m_view->currentTime() + newZoom > m_view->zoomFactor() && !m_mutex)
|
||||
{
|
||||
m_mutex = true;
|
||||
m_timer->start(1000);
|
||||
m_view->setEndTime(m_view->currentTime() + newZoom);
|
||||
}
|
||||
m_view->setZoomFactor(newZoom);
|
||||
return;
|
||||
}
|
||||
|
||||
m_view->setEndTime(std::max(m_view->endTime(), end));
|
||||
m_view->setCurrentTime(m_view->endTime() - m_view->zoomFactor());
|
||||
}
|
||||
|
||||
|
||||
void ScriptDialogDragHandler::addFilterItemsToDragItems()
|
||||
{
|
||||
for (const int &itemId : qAsConst(m_dragItemIds))
|
||||
{
|
||||
try
|
||||
{
|
||||
const Item &item = m_model->item(itemId);
|
||||
const Track &track = m_model->track(item.parentTrackId());
|
||||
if (track.hasTracks())
|
||||
{
|
||||
for (const TrackPtr &trackPtr : track.tracks())
|
||||
{
|
||||
const QVector<int> itemIds = trackPtr->itemIdsBetween(item.startTime(), item.startTime() + item.duration(), false, true);
|
||||
for (const int &id : qAsConst(itemIds))
|
||||
if (!m_dragItemIds.contains(id))
|
||||
m_dragItemIds.append(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptDialogDragHandler::setCanDropTopicTypes()
|
||||
{
|
||||
m_canDropTopicTypes = true;
|
||||
for (const int &itemId : qAsConst(m_dragItemIds))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!m_model->canDropTopicType(m_model->topicItem(itemId).topicType(), dragItemTrackId(m_model->item(itemId))))
|
||||
{
|
||||
m_canDropTopicTypes = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptDialogDragHandler::canDropTopicItemAt(const QPoint &point) const
|
||||
{
|
||||
if (!m_isDragEventFromTopicEditor)
|
||||
return TimelineViewDragHandler::canDragItemsTo(point);
|
||||
|
||||
const QModelIndex index = m_view->indexAt(point);
|
||||
if (!index.isValid() || index.column() == Section::eActuator)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
qint64 start = m_model->snapTime(m_view->toTime(point.x() - m_dragItemOffset));
|
||||
if (m_model->track(index.internalId()).isItemsBetween(start, start + m_dragItem->duration(), QVector<int>(), true))
|
||||
return false;
|
||||
return m_model->canDropItem(*m_dragItem.get(), index.internalId(), start);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include "pre_topic_item.h"
|
||||
#include "pre_utils.h"
|
||||
|
||||
#include <LtsTimeline/timeline_view_drag_handler.h>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class ScriptDelegate;
|
||||
class ScriptModel;
|
||||
|
||||
|
||||
class ScriptDialogDragHandler : public QObject, public LtsTimeline::TimelineViewDragHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class ScriptDelegate;
|
||||
|
||||
public:
|
||||
explicit ScriptDialogDragHandler(LtsTimeline::TimelineView *view);
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent *e) override;
|
||||
void dragMoveEvent(QDragMoveEvent *e) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent *e) override;
|
||||
void dropEvent(QDropEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
int deltaX() const override;
|
||||
int deltaY() const override;
|
||||
|
||||
int dragItemFromTopicEditorWidth() const;
|
||||
|
||||
const QVector<int> &dragItemIds() const { return m_dragItemIds; }
|
||||
|
||||
bool isDragEventFromTopicEditor() const { return m_isDragEventFromTopicEditor; }
|
||||
|
||||
private:
|
||||
void moveItemsLeft(const QVector<int> &, int dt) override;
|
||||
void moveItemsRight(const QVector<int> &, int dt) override;
|
||||
|
||||
int scrollOffset() const;
|
||||
qint64 minDragItemStart() const noexcept(false);
|
||||
qint64 maxDragItemEnd() const noexcept(false);
|
||||
void adjust() noexcept(false);
|
||||
void adjustLeft(qint64 start) noexcept(false);
|
||||
void adjustRight(qint64 end) noexcept(false);
|
||||
|
||||
void addFilterItemsToDragItems();
|
||||
void setCanDropTopicTypes();
|
||||
bool canDropTopicItemAt(const QPoint &point) const;
|
||||
|
||||
private:
|
||||
ScriptModel *m_model{nullptr};
|
||||
|
||||
TopicItemPtr m_dragItem;
|
||||
TopicEditorBase::TopicData m_dragTopicData;
|
||||
int m_dragItemOffset{0};
|
||||
bool m_isDragEventFromTopicEditor{false};
|
||||
bool m_canDropTopicTypes{true};
|
||||
|
||||
bool m_mutex{false};
|
||||
QTimer *m_timer{nullptr};
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
#include "pre_script_dialog_resize_handler.h"
|
||||
#include "pre_script_dialog.h"
|
||||
#include "pre_script_model.h"
|
||||
#include "pre_topic_item.h"
|
||||
|
||||
#include <LtsTimeline/timeline_view.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
ScriptDialogResizeHandler::ScriptDialogResizeHandler(LtsTimeline::TimelineView *view)
|
||||
: LtsTimeline::TimelineViewResizeHandler(view)
|
||||
, m_timer(new QTimer(m_view))
|
||||
{
|
||||
m_model = qobject_cast<ScriptModel *>(m_view->model());
|
||||
Q_ASSERT(m_model);
|
||||
|
||||
m_timer->setSingleShot(true);
|
||||
connect(m_timer, &QTimer::timeout, this, [this]()
|
||||
{
|
||||
m_mutex = false;
|
||||
});
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC
|
||||
|
||||
void ScriptDialogResizeHandler::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
LtsTimeline::TimelineViewResizeHandler::mouseMoveEvent(e);
|
||||
if (m_currentItemId == -1)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
TopicItem &topicItem = m_model->topicItem(m_currentItemId);
|
||||
topicItem.topicData().duration = topicItem.duration() / 1000;
|
||||
emit m_model->topicItemChanged(topicItem.id());
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << errMsg; }
|
||||
catch (...) { qCritical() << "could not update topic item with id =" << m_currentItemId; }
|
||||
|
||||
if (e->buttons() != Qt::LeftButton)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
const LtsTimeline::Item &item = m_model->item(m_currentItemId);
|
||||
if (m_resizePos == ResizePosition::eStart)
|
||||
{
|
||||
if (item.startTime() < m_view->currentTime() + scrollOffset() && m_view->startTime() < m_view->currentTime())
|
||||
adjustLeft();
|
||||
}
|
||||
else if (m_resizePos == ResizePosition::eEnd)
|
||||
{
|
||||
if (item.startTime() + item.duration() > m_view->currentTime() + m_view->zoomFactor() - scrollOffset())
|
||||
adjustRight(item);
|
||||
}
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "adjusting zoom failed:" << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION << "adjusting zoom failed"; }
|
||||
}
|
||||
|
||||
void ScriptDialogResizeHandler::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
if (m_timer->isActive())
|
||||
m_timer->stop();
|
||||
m_mutex = false;
|
||||
|
||||
try
|
||||
{
|
||||
m_view->setEndTime(std::max(ScriptModel::s_minScriptDuration,
|
||||
static_cast<ScriptModel *>(m_view->model())->lastTopicEnd() + ScriptModel::s_scriptEndOffset));
|
||||
const LtsTimeline::Item &item = m_model->item(m_currentItemId);
|
||||
qint64 end = item.startTime() + item.duration() + scrollOffset();
|
||||
if (end > m_view->currentTime() + m_view->zoomFactor())
|
||||
{
|
||||
ZoomFactor newZoom = Zoom::increment(m_view->zoomFactor());
|
||||
if (newZoom < m_view->maximumZoomFactor() && newZoom <= m_view->duration())
|
||||
m_view->setZoomFactor(newZoom);
|
||||
else
|
||||
m_view->setCurrentTime(end - m_view->zoomFactor());
|
||||
}
|
||||
}
|
||||
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "adjusting zoom failed:" << errMsg; }
|
||||
catch (...) { qCritical() << LOGFUNCTION << "adjusting zoom failed";}
|
||||
|
||||
TimelineViewResizeHandler::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE
|
||||
|
||||
int ScriptDialogResizeHandler::scrollOffset() const
|
||||
{
|
||||
return m_view->zoomFactor() * 5 / 100;
|
||||
}
|
||||
|
||||
void ScriptDialogResizeHandler::adjustLeft()
|
||||
{
|
||||
ZoomFactor newZoom = Zoom::increment(m_view->zoomFactor());
|
||||
if (m_view->zoomFactor() < newZoom && newZoom <= m_view->maximumZoomFactor())
|
||||
{
|
||||
m_view->setCurrentTime(m_view->currentTime() - (newZoom - m_view->zoomFactor()));
|
||||
m_view->setZoomFactor(newZoom);
|
||||
return;
|
||||
}
|
||||
|
||||
m_view->setCurrentTime(m_view->currentTime() - m_view->zoomFactor() * 2 / 100);
|
||||
}
|
||||
|
||||
void ScriptDialogResizeHandler::adjustRight(const LtsTimeline::Item &item)
|
||||
{
|
||||
ZoomFactor newZoom = Zoom::increment(m_view->zoomFactor());
|
||||
if (m_view->zoomFactor() < newZoom && newZoom <= m_view->maximumZoomFactor())
|
||||
{
|
||||
if (m_view->currentTime() + newZoom > m_view->endTime() && !m_mutex)
|
||||
{
|
||||
m_mutex = true;
|
||||
m_timer->start(1000);
|
||||
m_view->setEndTime(std::max(ScriptModel::s_minScriptDuration, m_view->currentTime() + newZoom));
|
||||
}
|
||||
m_view->setZoomFactor(newZoom);
|
||||
return;
|
||||
}
|
||||
|
||||
m_view->setEndTime(std::max(std::max(ScriptModel::s_minScriptDuration, m_view->endTime()),
|
||||
item.startTime() + item.duration() + ScriptModel::s_scriptEndOffset));
|
||||
m_view->setCurrentTime(m_view->endTime() - m_view->zoomFactor());
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <LtsTimeline/timeline_view_resize_handler.h>
|
||||
|
||||
|
||||
namespace LtsTimeline { class Item; }
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class ScriptModel;
|
||||
|
||||
|
||||
class ScriptDialogResizeHandler : public QObject, public LtsTimeline::TimelineViewResizeHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ScriptDialogResizeHandler(LtsTimeline::TimelineView *view);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
private:
|
||||
int scrollOffset() const;
|
||||
void adjustLeft();
|
||||
void adjustRight(const LtsTimeline::Item &item);
|
||||
|
||||
private:
|
||||
ScriptModel *m_model{nullptr};
|
||||
|
||||
QTimer *m_timer{nullptr};
|
||||
bool m_mutex{false};
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
#include "pre_script_dialog_select_handler.h"
|
||||
#include "pre_script_model.h"
|
||||
|
||||
#include <QToolTip>
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
ScriptDialogSelectHandler::ScriptDialogSelectHandler(LtsTimeline::TimelineView *view)
|
||||
: TimelineViewSelectHandler(view)
|
||||
{}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLC
|
||||
|
||||
void ScriptDialogSelectHandler::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
LtsTimeline::TimelineViewSelectHandler::mouseMoveEvent(e);
|
||||
|
||||
// hover text
|
||||
ScriptModel *model = static_cast<ScriptModel *>(m_view->timelineModel());
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
QModelIndex index = m_view->indexAt(e->pos());
|
||||
if (!index.isValid() || index.column() == LtsTimeline::Section::eActuator)
|
||||
{
|
||||
m_view->setCursorTextVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
qint64 time = m_view->toTime(e->pos().x());
|
||||
const LtsTimeline::Track &track = model->track(index.internalId());
|
||||
TopicItem &topicItem = model->topicItem(track.itemAt(time).id());
|
||||
qint64 replayTime = topicItem.replayTime(time);
|
||||
if (replayTime != -1)
|
||||
{
|
||||
m_view->setCursorTextVisible(true);
|
||||
m_view->setCursorText(LtsTimeline::DateTime::toString(replayTime, LtsTimeline::DateTimeFormat::DTG));
|
||||
}
|
||||
else
|
||||
m_view->setCursorTextVisible(false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
m_view->setCursorTextVisible(false);
|
||||
}
|
||||
|
||||
// tooltip
|
||||
try
|
||||
{
|
||||
const LtsTimeline::Track &track = m_view->timelineModel()->track(index.internalId());
|
||||
const LtsTimeline::Item &item = track.itemAt(m_view->toTime(e->pos().x()));
|
||||
m_view->setToolTip(LtsTimeline::DateTime::toString(item.startTime(), LtsTimeline::DateTimeFormat::TimeOnly));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (!m_view->toolTip().isEmpty())
|
||||
{
|
||||
QToolTip::hideText();
|
||||
m_view->setToolTip({});
|
||||
m_view->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <LtsTimeline/timeline_view_selection_handler.h>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class ScriptDialogSelectHandler : public LtsTimeline::TimelineViewSelectHandler
|
||||
{
|
||||
|
||||
public:
|
||||
explicit ScriptDialogSelectHandler(LtsTimeline::TimelineView *view);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+1561
File diff suppressed because it is too large.
Load diff
+146
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
#include "pre_topic_item.h"
|
||||
#include "pre_topic_track.h"
|
||||
|
||||
#include <LtsTimeline/timeline_model.h>
|
||||
|
||||
|
||||
namespace LtsSession { class LtsSession; }
|
||||
|
||||
using namespace LtsTimeline;
|
||||
using namespace TopicEditorBase;
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class PresentationContent;
|
||||
class ScriptTimelineDialog;
|
||||
|
||||
|
||||
class ScriptModel : public LtsTimeline::TimelineModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class PasteTestResult { No = 0, Yes, YesWithShift };
|
||||
|
||||
static bool isReplayTopic(TopicType topicType);
|
||||
static bool isScreenTopic(TopicType topicType);
|
||||
static bool isFilterTopic(TopicType topicType);
|
||||
static bool hasReplayTime(TopicType topicType);
|
||||
|
||||
static const qint64 s_minScriptDuration;
|
||||
static const int s_scriptEndOffset;
|
||||
|
||||
explicit ScriptModel(LtsSession::LtsSession *session, ScriptTimelineDialog *dialog, QObject *parent = nullptr);
|
||||
~ScriptModel() = default;
|
||||
|
||||
void clear();
|
||||
|
||||
int addItem(int _trackId, TopicItem &&_topicItem) noexcept(false);
|
||||
int addItem(const QString &trackName, TopicItem &&_topicItem, qint64 offset = 0) noexcept(false);
|
||||
int nextUniqueId() { return m_currentItemId + 1; }
|
||||
|
||||
TopicItem &topicItem(int _topicItemId) noexcept(false);
|
||||
const TopicItem &topicItem(int _topicItemId) const noexcept(false);
|
||||
std::vector<TopicItem> topicItems() const;
|
||||
std::vector<TopicItem> topicItemsByTrackId(int trackId) const;
|
||||
QVector<int> topicItemIds() const { return mainTopicItemIds() + filterTopicItemIds(); }
|
||||
|
||||
QVector<int> mainTopicItemIds() const;
|
||||
int mainTopicItemId(const Track &_track, qint64 start, qint64 end) const noexcept(false);
|
||||
int mainTopicItemId(const Track &_track, const TopicItem &_topicItem) const noexcept(false)
|
||||
{
|
||||
return mainTopicItemId(_track, _topicItem.startTime(), _topicItem.startTime() + _topicItem.duration());
|
||||
}
|
||||
int mainTopicItemId(const TopicItem &_topicItem) const noexcept(false);
|
||||
int mainTopicItemId(int _topicItemId) const noexcept(false)
|
||||
{
|
||||
return mainTopicItemId(topicItem(_topicItemId));
|
||||
}
|
||||
|
||||
QVector<int> filterTopicItemIds() const;
|
||||
QVector<int> filterTopicItemIds(int _topicItemId) const;
|
||||
|
||||
qint64 firstTopicStart() const;
|
||||
qint64 lastTopicEnd() const;
|
||||
|
||||
TopicTrack &audioTrack() noexcept(false) { return static_cast<TopicTrack &>(track(m_audioTrackId)); }
|
||||
const TopicTrack &audioTrack() const noexcept(false) { return static_cast<const TopicTrack &>(track(m_audioTrackId)); }
|
||||
TopicTrack &replayTrack() noexcept(false) { return static_cast<TopicTrack &>(track(m_replayTrackId)); }
|
||||
const TopicTrack &replayTrack() const noexcept(false) { return static_cast<const TopicTrack &>(track(m_replayTrackId)); }
|
||||
TopicTrack &screen1Track() noexcept(false) { return static_cast<TopicTrack &>(track(m_screen1TrackId)); }
|
||||
const TopicTrack &screen1Track() const noexcept(false) { return static_cast<const TopicTrack &>(track(m_screen1TrackId)); }
|
||||
TopicTrack &screen2Track() noexcept(false) { return static_cast<TopicTrack &>(track(m_screen2TrackId)); }
|
||||
const TopicTrack &screen2Track() const noexcept(false) { return static_cast<const TopicTrack &>(track(m_screen2TrackId)); }
|
||||
|
||||
int channelToId(const QString &channel) const;
|
||||
int audioTrackId() const { return m_audioTrackId; }
|
||||
int replayTrackId() const { return m_replayTrackId; }
|
||||
int screen1TrackId() const { return m_screen1TrackId; }
|
||||
int screen2TrackId() const { return m_screen2TrackId; }
|
||||
|
||||
bool isMainTrack(int _trackId) const;
|
||||
bool isScreenTrack(int _trackId) const;
|
||||
bool isFilterTrack(int _trackId) const;
|
||||
|
||||
bool isReplayTopic(const TopicItem &_topicItem) const { return isReplayTopic(_topicItem.topicType()); }
|
||||
bool isScreenTopic(const TopicItem &_topicItem) const { return isScreenTopic(_topicItem.topicType()); }
|
||||
bool isFilterTopic(const TopicItem &_topicItem) const { return isFilterTopic(_topicItem.topicType()); }
|
||||
|
||||
// bool canBeMerged(const TopicItem &_firstTopicItem, const TopicItem &_secondTopicItem, const TopicItem &_thirdTopicItem) const;
|
||||
// bool canBeSplit(const TopicItem &_topicItem) const;
|
||||
// void mergeTopicItems(int _firstTopicItemId, int _secondTopicItemId, int _thirdTopicItemId) noexcept(false);
|
||||
// void splitTopicItem(int _topicItemId) noexcept(false);
|
||||
|
||||
bool import(const QVector<PresentationContent> &contents) noexcept(false);
|
||||
PresentationContent topicItemPresentationContent(int topicItemId) noexcept(false);
|
||||
|
||||
void insertTopicItem(int _trackId, TopicItem &&__topicItem);
|
||||
|
||||
void moveItemToTrack(int _itemId, int _trackId, qint64 time) noexcept(false) override;
|
||||
void removeItem(int _itemId) noexcept(false) override;
|
||||
void setItemStartTime(Track &_track, Item &_item, qint64 newStart) override;
|
||||
void setItemDuration(Track &_track, Item &_item, int duration) override;
|
||||
|
||||
bool canDropItem(const Item &_item, int _trackId, qint64 start) const override;
|
||||
bool canMoveItem(const Item &_item, qint64 start) const override;
|
||||
bool canResizeItem(const Item &_item, qint64 start, int duration) const override;
|
||||
|
||||
void copyToClipboard(const QVector<int> &_itemIds) noexcept(false) override;
|
||||
void cutToClipboard(const QVector<int> &_itemIds) noexcept(false) override;
|
||||
void pasteFromClipboard(qint64 time) noexcept(false) override;
|
||||
PasteTestResult canPasteFromClipboard(qint64 time) const;
|
||||
|
||||
bool canDropTopicType(TopicType _topicType, int _trackId) const;
|
||||
|
||||
private:
|
||||
bool isTopicItemBlocked(const TopicItem &_topicItem, const TopicTrack &_topicTrack, qint64 start, int duration) const;
|
||||
|
||||
void getActualFilterTopicRange(int parentTrackId, TopicItem &_topicItem) const noexcept(false);
|
||||
|
||||
public slots:
|
||||
void onInsertTrack(const QModelIndex &idx) override;
|
||||
void onRemoveTrack(const QModelIndex &idx) override;
|
||||
|
||||
signals:
|
||||
void topicItemChanged(int);
|
||||
void pasteFailed();
|
||||
|
||||
private:
|
||||
LtsSession::LtsSession *m_session{nullptr};
|
||||
|
||||
ScriptTimelineDialog *m_dialog{nullptr};
|
||||
|
||||
int m_audioTrackId{-1};
|
||||
int m_replayTrackId{-1};
|
||||
int m_screen1TrackId{-1};
|
||||
int m_screen2TrackId{-1};
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* GUI Plugin for Script Editor
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-03-17
|
||||
*/
|
||||
|
||||
#include "pre_script_timeline_gui_plugin.h"
|
||||
#include "pre_script_dialog.h"
|
||||
#include "pre_script_model.h"
|
||||
|
||||
#include <Common/TopicEditorBase/topic_editor_base.h>
|
||||
#include <Plugins/OperatorManagement/opm_data_plugin_interface.h>
|
||||
#include <Plugins/PresentationManagement/Gui/Script/Common/ScriptChooser/script_chooser.h>
|
||||
#include <Plugins/PresentationManagement/Gui/Common/pre_edit_dialog.h>
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsSession/lts_session_manager.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
#include <LtsWidgets/lts_background_widget.h>
|
||||
#include <LtsWidgets/lts_message_dialog.h>
|
||||
#include <LtsScriptTimeline/track_entry.h>
|
||||
#include <LtsScriptTimeline/temporary_pointer_vector.h>
|
||||
#include <TopicEditorBase/topic_editor_utils.h>
|
||||
|
||||
// Control- & Widget-Ifcs:
|
||||
#include <Plugins/SituationReplay/srp_gui_plugin_interface.h>
|
||||
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
|
||||
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
|
||||
#include <Plugins/BuildingViewer/bdv_viewer_gui_plugin_interface.h>
|
||||
#include <Plugins/AudioVideoManagement/avm_menu_edit_gui_plugin_interface.h>
|
||||
#include <Plugins/SituationMapPlugin/situation_map_script_filter_gui_plugin_interface.h>
|
||||
#include <Plugins/MarkerManagement/mrk_gui_plugin_interface.h>
|
||||
#include <Plugins/Evaluations/evl_gui_plugin_interface.h>
|
||||
#include <Plugins/ReportManagement/rep_gui_plugin_interface.h>
|
||||
#include <Plugins/PlanOverlays/plan_overlays_plugin_interface.h>
|
||||
|
||||
#include <Services/PresentationManagement/pre_operator_rights.h>
|
||||
#include <Services/PresentationManagement/pre_dtc.h>
|
||||
|
||||
#include <stdlib.h> // for abort
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonDocument>
|
||||
#include <QProgressDialog>
|
||||
#include <QTimer>
|
||||
#include <QFileDialog>
|
||||
#include <QVBoxLayout>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
|
||||
{
|
||||
return std::make_shared<InterfaceImpl>(session);
|
||||
}
|
||||
|
||||
|
||||
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
|
||||
: GuiPluginBase::Interface(session, getLogger("Plugins.Gui.Pre.ScriptTimeline"))
|
||||
, m_preDtc(nullptr)
|
||||
, m_preFtc(nullptr)
|
||||
, m_opmDtc(nullptr)
|
||||
, m_srpIfc(nullptr)
|
||||
, m_scriptId(0) // invalid
|
||||
, m_scriptDialog(nullptr)
|
||||
, m_requestHelper(new LtsUtils::RequestHelper(this))
|
||||
{
|
||||
connect(this, &InterfaceImpl::success, m_session, &LtsSession::LtsSession::slotExecuteNextPayload, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
||||
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
|
||||
{
|
||||
QVector<LtsWidgets::PluginData> ret;
|
||||
|
||||
{
|
||||
LtsWidgets::PluginData data;
|
||||
data.menuGroup = menuGroup();
|
||||
data.identifier = QStringLiteral("PresentationManagement.ScriptTimelineDialog");
|
||||
data.menuText = tr("Show Script Timeline Editor");
|
||||
if (getFlags().testFlag(LtsAppDescriptor::PluginInfo::Flag::MAIN))
|
||||
{
|
||||
data.layoutArea = LtsWidgets::LayoutArea::eMainTab;
|
||||
data.createInstant = LtsWidgets::CreateInstantType::CreateInstantAndDiscardAction;
|
||||
data.closable = false;
|
||||
}
|
||||
else // (as dialog) = rather unlikely..
|
||||
{
|
||||
data.layoutArea = LtsWidgets::LayoutArea::eDialogTab;
|
||||
}
|
||||
data.operatorRight = OperatorRights::ScriptEditorShow;
|
||||
data.createWidgetFunc = [this](QWidget *parent)
|
||||
{
|
||||
return onShowScriptDialog(parent);
|
||||
};
|
||||
ret.append(data);
|
||||
}
|
||||
|
||||
{
|
||||
LtsWidgets::PluginData data;
|
||||
data.menuGroup = menuGroup();
|
||||
data.identifier = QStringLiteral("PresentationManagement.ScriptDialog.ImportScript");
|
||||
data.menuIcon = QIcon(QStringLiteral("icon:import_documents.svg"));
|
||||
data.menuText = tr("Insert Script");
|
||||
data.operatorRight = OperatorRights::ScriptEditorEdit;
|
||||
data.directFunc = [this](const LtsWidgets::PluginData &)
|
||||
{
|
||||
return onImportScript();
|
||||
};
|
||||
data.createWidgetFunc = nullptr;
|
||||
ret.append(data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::fetchInterfaces()
|
||||
{
|
||||
if (!m_session->getDtc<PresentationManagement::DataInterface>(m_preDtc))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "getDtc for PresentationManagement::DataInterface failed.";
|
||||
return false;
|
||||
}
|
||||
if (!m_session->getFtc<PresentationManagement::DataInterface>(m_preFtc))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "getFtc for PresentationManagement::DataInterface failed.";
|
||||
return false;
|
||||
}
|
||||
if (!m_session->getDtc<SituationReplayManagement::DataInterface>(m_coreDtc))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "getDtc for SituationReplayManagement::DataInterface failed.";
|
||||
return false;
|
||||
}
|
||||
if (!m_session->getDtc<OperatorManagement::DataInterface>(m_opmDtc))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "getDtc for OperatorManagement::DataInterface failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_session->getDtc<ExerciseManagement::DataInterface>(m_exmDtc))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "getDtc for OperatorManagement::DataInterface failed.";
|
||||
return false;
|
||||
}
|
||||
m_exmDtc->enableExercise();
|
||||
|
||||
if (!m_session->getForeignDataInterface<SituationReplayManagement::DataInterface>(m_srpIfc))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "getForeignDataInterface for SituationReplayManagement::DataInterface failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InterfaceImpl::clearInterfaces()
|
||||
{
|
||||
m_preDtc = nullptr;
|
||||
m_preFtc = nullptr;
|
||||
m_coreDtc = nullptr;
|
||||
m_opmDtc = nullptr;
|
||||
m_exmDtc = nullptr;
|
||||
m_srpIfc = nullptr;
|
||||
}
|
||||
|
||||
QString InterfaceImpl::menuGroup() const
|
||||
{
|
||||
return tr("Presentation");
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::executePayload(LtsWidgets::LtsBackgroundWidget *mainBackground)
|
||||
{
|
||||
Q_UNUSED(mainBackground);
|
||||
|
||||
if (m_session->partitionType() != IDL::PartitionType::Exercise) // only on valid exercise
|
||||
{
|
||||
emit success();
|
||||
return;
|
||||
}
|
||||
|
||||
int scriptId = m_session->getSavegame()[QStringLiteral("ScriptId")].toInt();
|
||||
if (scriptId > 0)
|
||||
{
|
||||
onSelectScript(scriptId);
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptChooser *scriptChooser = new ScriptChooser(m_preDtc,m_preFtc, m_opmDtc, false, false, mainBackground);
|
||||
scriptChooser->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
connect(scriptChooser, &ScriptChooser::signalSelectScript,
|
||||
this, &InterfaceImpl::onSelectScript, Qt::QueuedConnection);
|
||||
|
||||
connect(scriptChooser, &ScriptChooser::signalQuit,
|
||||
m_session, &LtsSession::LtsSession::signalQuit);
|
||||
|
||||
connect(scriptChooser, &ScriptChooser::signalNewScript,
|
||||
this, [this, scriptChooser]()
|
||||
{
|
||||
PresentationEditDialog *dialog = new PresentationEditDialog(nullptr);
|
||||
if (!dialog->init(-1 /* new presentation */, m_preDtc, m_preFtc, m_opmDtc))
|
||||
{
|
||||
delete dialog;
|
||||
return;
|
||||
}
|
||||
QObject::connect(dialog, &PresentationEditDialog::rejected, dialog, &PresentationEditDialog::deleteLater);
|
||||
QObject::connect(dialog, &PresentationEditDialog::newScript, this, &InterfaceImpl::onSelectScript, Qt::QueuedConnection);
|
||||
QObject::connect(dialog, &PresentationEditDialog::newScript, scriptChooser, &QObject::deleteLater);
|
||||
dialog->show();
|
||||
});
|
||||
|
||||
if (!LtsSessionData::LtsSessionData::getInstance()->isTablet())
|
||||
scriptChooser->setModal(true);
|
||||
|
||||
scriptChooser->show();
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::importScript(int iSource)
|
||||
{
|
||||
if (!m_scriptDialog)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "no script dialog open for import script";
|
||||
return false;
|
||||
}
|
||||
|
||||
PresentationManagement::Presentation presentation = m_preDtc->presentation(iSource);
|
||||
if (presentation.id() < 1)
|
||||
return false;
|
||||
|
||||
// import topics
|
||||
std::function<void(const Core::FunctionReply &baseReply)> importPresentationContent = [&](const Core::FunctionReply &baseReply)
|
||||
{
|
||||
const GetPresentationContentReply &reply = static_cast<const GetPresentationContentReply &>(baseReply);
|
||||
const auto &contentList = reply.content();
|
||||
|
||||
if (contentList.empty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "No Presentation Content for Script Id " << reply.id();
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<PresentationContent> contents;
|
||||
contents.reserve(contentList.size());
|
||||
for (const PresentationContent &content : contentList)
|
||||
{
|
||||
TopicEditorBase::TopicType topicType = TopicEditorBase::topicTypeFromString(QString::fromStdString(content.presentationTopicType()));
|
||||
if (topicType == TopicEditorBase::TopicType::Invalid)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Topic ignored - invalid topic type for presentation's content id = " << content.id();
|
||||
continue;
|
||||
}
|
||||
contents.append(content);
|
||||
}
|
||||
m_scriptDialog->import(contents);
|
||||
};
|
||||
|
||||
GetPresentationContentRequest req(iSource);
|
||||
req.presentationId(iSource);
|
||||
if (!m_requestHelper->startSingleRequest([req, this](){ return m_preFtc->getPresentationContentRequest(req, this); }, importPresentationContent))
|
||||
{
|
||||
LtsWidgets::LtsMessageDialog::showMessage(m_session->baseLayout(), tr("There is no content to import for this script."));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
LtsWidgets::LtsDialog *InterfaceImpl::onShowScriptDialog(QWidget *parent)
|
||||
{
|
||||
|
||||
m_scriptDialog = new ScriptTimelineDialog(menuGroup(), parent);
|
||||
// m_scriptDialog = new ScriptTimelineDialog(
|
||||
// menuGroup(), m_session, m_scriptId, m_coreDtc, m_opmDtc, m_preDtc, m_preFtc, m_srpIfc, parent
|
||||
// );
|
||||
//const int exId = m_session->exerciseId();
|
||||
//ExerciseManagement::Exercise exercise = m_exmDtc->exercise(exId);
|
||||
|
||||
//DEBUG_LOG(m_logger) << "### ###ExId: " << exId << " ExName: " << exercise.name();
|
||||
m_scriptDialog->init(m_session, m_scriptId, m_preDtc, m_preFtc, m_srpIfc, m_coreDtc, m_opmDtc, m_exmDtc);
|
||||
connect(m_scriptDialog, &ScriptTimelineDialog::scriptChanged, this, [this](int scriptId) { m_scriptId = scriptId; });
|
||||
return m_scriptDialog.data();
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::onImportScript()
|
||||
{
|
||||
ScriptChooser *scriptChooser = new ScriptChooser(m_preDtc, m_preFtc, m_opmDtc, false, true, m_session->baseLayout());
|
||||
scriptChooser->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(scriptChooser, &ScriptChooser::signalSelectScript, this, &InterfaceImpl::onImportSpecificScript, Qt::QueuedConnection);
|
||||
connect(scriptChooser, &ScriptChooser::signalQuit, m_session, &LtsSession::LtsSession::signalQuit);
|
||||
if (!LtsSessionData::LtsSessionData::getInstance()->isTablet())
|
||||
scriptChooser->setModal(true);
|
||||
scriptChooser->show();
|
||||
}
|
||||
|
||||
void InterfaceImpl::onImportSpecificScript(int scriptId)
|
||||
{
|
||||
if (m_scriptId == scriptId)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Not possible to import same script!";
|
||||
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("Not possible to import same script!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!importScript(scriptId))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Import of script with id = " << scriptId << " failed";
|
||||
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("Failed to import Script with Id: %1").arg(scriptId));
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "Succesfully imported script with id = " << scriptId;
|
||||
//LtsWidgets::LtsMessageDialog::showMessage(nullptr, tr("Successfully imported Script Id %1").arg(scriptId));
|
||||
}
|
||||
|
||||
void InterfaceImpl::onSelectScript(int scriptId)
|
||||
{
|
||||
m_scriptId = scriptId;
|
||||
// std::shared_ptr<ForeignGuiInterface> preGuiIfc;
|
||||
// if (!m_session->getForeignGuiInterface<ForeignGuiInterface>(preGuiIfc))
|
||||
// {
|
||||
// // TODO: MSG !!1
|
||||
// return;
|
||||
// }
|
||||
// preGuiIfc->setScriptId(scriptId);
|
||||
emit success();
|
||||
}
|
||||
|
||||
|
||||
} // namespace PresentationManagement
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* GUI Plugin for Script Editor
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-03-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <PresentationManagement/logger.h>
|
||||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||||
#include <Plugins/PresentationManagement/pre_data_plugin_interface.h>
|
||||
#include <Plugins/PresentationManagement/pre_script_timeline_gui_plugin_interface.h>
|
||||
#include <Services/PresentationManagement/pre_ftc.h> // reply handler
|
||||
#include <Services/OperatorManagement/opm_dtc.h>
|
||||
#include <ExerciseManagement/exm_data_plugin_interface.h>
|
||||
#include <ExerciseManagement/exm_dtc.h>
|
||||
#include <Plugins/SituationReplayManagement/srm_data_plugin_interface.h>
|
||||
#include <Common/ProjectConfig/project_defines.h>
|
||||
#include <LtsUtilsGuiDds/request_helper.h>
|
||||
// test:
|
||||
#include <LtsScriptTimeline/track_entry_painters.h>
|
||||
#include "pre_script_dialog.h"
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||||
#include <PluginInterfaces/topic_replay_interface.h>
|
||||
#include <Plugins/PresentationManagement/Gui/Common/topic_assignment.h>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "gui:3:pre.script_timeline_edit")
|
||||
Q_INTERFACES(GuiPluginBase::Plugin)
|
||||
|
||||
public:
|
||||
explicit GuiPlugin() = default;
|
||||
~GuiPlugin() = default;
|
||||
|
||||
QString pluginName() override { return ForeignScriptTimelineDialogGuiInterface::pluginName(); }
|
||||
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
|
||||
bool availableInPartition(IDL::PartitionType partition) const override { return (partition == IDL::PartitionType::Exercise); }
|
||||
};
|
||||
|
||||
|
||||
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface
|
||||
, protected PresentationManagement::Interface::Ftc::PresentationModifyReplyHandler
|
||||
, protected PresentationManagement::Interface::Ftc::GetPresentationContentReplyHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InterfaceImpl(LtsSession::LtsSession *session);
|
||||
|
||||
QVector<LtsWidgets::PluginData> getActions() override;
|
||||
QString menuGroup() const override;
|
||||
bool fetchInterfaces() override;
|
||||
void clearInterfaces() override;
|
||||
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Exercise; }
|
||||
|
||||
// be able to show script chooser
|
||||
int executionPriority() const override { return 9999; }
|
||||
void executePayload(LtsWidgets::LtsBackgroundWidget *mainBackground) override;
|
||||
|
||||
private:
|
||||
void onImportScript();
|
||||
bool importScript(/*int iDestination, */int iSource);
|
||||
|
||||
protected slots:
|
||||
void onPresentationModifyReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
|
||||
void onGetPresentationContentReply(const GetPresentationContentReply &reply) override { m_requestHelper->requestReply(reply); }
|
||||
|
||||
private slots:
|
||||
LtsWidgets::LtsDialog *onShowScriptDialog(QWidget *parent);
|
||||
|
||||
void onSelectScript(int scriptId);
|
||||
void onImportSpecificScript(int scriptId);
|
||||
|
||||
signals:
|
||||
void success();
|
||||
|
||||
private:
|
||||
std::shared_ptr<PresentationManagement::Interface::Dtc> m_preDtc { nullptr };
|
||||
std::shared_ptr<PresentationManagement::Interface::Ftc> m_preFtc { nullptr };
|
||||
std::shared_ptr<Core::Interface::Dtc> m_coreDtc { nullptr };
|
||||
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc { nullptr };
|
||||
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc { nullptr };
|
||||
|
||||
|
||||
std::shared_ptr<SituationReplayManagement::ForeignDataInterface> m_srpIfc { nullptr };
|
||||
|
||||
|
||||
int m_scriptId { 0 };
|
||||
QPointer<PresentationManagement::ScriptTimelineDialog> m_scriptDialog { nullptr };
|
||||
LtsUtils::RequestHelper *m_requestHelper { nullptr };
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
#include "pre_storyboard.h"
|
||||
#include "ui_pre_storyboard.h"
|
||||
#include "LtsPrinting/printing_base.h"
|
||||
#include <LtsPrinting/printing.h>
|
||||
#include "pre_script_dialog_select_handler.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QLabel>
|
||||
#include <QJsonArray>
|
||||
#include <QTextTable>
|
||||
#include <QFileDialog>
|
||||
#include <QPrinter>
|
||||
#include <QPainter>
|
||||
#include <QPrintDialog>
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
#include <LtsWidgets/lts_base_layout_widget.h>
|
||||
|
||||
#include <LtsScriptTimeline/track_entry.h>
|
||||
#include <ngltools/ngltools_dtg.h>
|
||||
#include <Services/PresentationManagement/logger.h>
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
StoryboardDialog::StoryboardDialog(
|
||||
QWidget *parent, LtsSession::LtsSession *session, const QString &scriptName, const QString &scriptOwner,
|
||||
const QString &maxTopicTimeString, qint64 lastModified, std::vector<TopicItem> topicItems
|
||||
)
|
||||
: LtsWidgets::LtsDialog(parent)
|
||||
, m_session(session)
|
||||
, m_scriptName(scriptName)
|
||||
, m_scriptOwner(scriptOwner)
|
||||
, m_maxTopicTimeString(maxTopicTimeString)
|
||||
, m_lastModified(lastModified)
|
||||
, m_isAlreadyTranslated(false)
|
||||
, m_ui(new Ui::StoryboardDialog)
|
||||
, m_logger(getLogger(QStringLiteral("StoryboardDialog")))
|
||||
{
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Storyboard"));
|
||||
|
||||
m_ui->setupUi(this);
|
||||
m_ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
||||
m_ui->tableWidget->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
|
||||
m_ui->tableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &StoryboardDialog::onPrint);
|
||||
|
||||
onColumnWidthChanged();
|
||||
connect(m_ui->tableWidget->horizontalHeader(), &QHeaderView::sectionResized, this, &StoryboardDialog::onColumnWidthChanged);
|
||||
fillStoryboard(std::move(topicItems));
|
||||
}
|
||||
|
||||
StoryboardDialog::~StoryboardDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC
|
||||
|
||||
void StoryboardDialog::retranslate()
|
||||
{
|
||||
translate(m_ui, tr("Script Storyboard"));
|
||||
if (m_isAlreadyTranslated)
|
||||
return;
|
||||
m_isAlreadyTranslated = true;
|
||||
}
|
||||
|
||||
void StoryboardDialog::setScriptData(const QString &scriptName, const QString &scriptOwner, const QString &maxTopicTimeString, qint64 lastModified)
|
||||
{
|
||||
m_scriptName = scriptName;
|
||||
m_scriptOwner = scriptOwner;
|
||||
m_maxTopicTimeString = maxTopicTimeString;
|
||||
m_lastModified = lastModified;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @details Fill the Storyboard with Data from Model, Sort the vector by time of topic
|
||||
* @param topicItems
|
||||
*/
|
||||
void StoryboardDialog::fillStoryboard(std::vector<TopicItem> topicItems)
|
||||
{
|
||||
m_ui->tableWidget->clear();
|
||||
m_ui->tableWidget->clearContents();
|
||||
m_ui->tableWidget->setRowCount(0);
|
||||
m_topicItems.clear();
|
||||
m_topicItems.reserve(topicItems.size());
|
||||
std::vector<TopicEditorBase::TopicData> dataVector;
|
||||
dataVector.reserve(topicItems.size());
|
||||
|
||||
for (TopicItem &topicItem : topicItems)
|
||||
m_topicItems.push_back(std::move(topicItem));
|
||||
|
||||
for (TopicItem &topicItem : m_topicItems)
|
||||
dataVector.push_back(topicItem.topicData());
|
||||
|
||||
std::sort(dataVector.begin(), dataVector.end(), [](const TopicEditorBase::TopicData &leftValue, const TopicEditorBase::TopicData &rightValue)
|
||||
{
|
||||
if (leftValue.startTime == rightValue.startTime)
|
||||
{
|
||||
if (leftValue.channel == QStringLiteral("Audio"))
|
||||
return true;
|
||||
else if (leftValue.channel == QStringLiteral("Replay") && rightValue.channel == QStringLiteral("Audio"))
|
||||
return false;
|
||||
else if (leftValue.channel == QStringLiteral("Screen 1") && (rightValue.channel == QStringLiteral("Replay") || rightValue.channel == QStringLiteral("Audio")))
|
||||
return false;
|
||||
else if (leftValue.channel == QStringLiteral("Screen 2") && (rightValue.channel == QStringLiteral("Replay") || rightValue.channel == QStringLiteral("Audio") || rightValue.channel == QStringLiteral("Screen 1")))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return leftValue.startTime < rightValue.startTime;
|
||||
});
|
||||
|
||||
int topicCounter = 1;
|
||||
|
||||
for (TopicEditorBase::TopicData &data : dataVector)
|
||||
{
|
||||
if (!data.parentChannel.isEmpty() || data.state != true || data.channel.isEmpty() || data.topicType == TopicEditorBase::TopicType::Invalid)
|
||||
continue;
|
||||
|
||||
QString firstColumnText = QStringLiteral("<big><b>[ %1 ]</b></big><br>%2").arg(topicCounter).arg(QTime::fromMSecsSinceStartOfDay(
|
||||
((dataVector.at(dataVector.size()-1).startTime * 1000) + (dataVector.at(dataVector.size()-1).duration * 1000)) -
|
||||
(data.startTime * 1000)).toString());
|
||||
QString secondColumnText = QStringLiteral("<big><b>%1</b></big>").arg(TopicEditorBase::topicTypeString(data.topicType));
|
||||
QString thirdColumnText = QStringLiteral("<big><b>​</b></big>"); // Creates empty line with the same height than the other columns
|
||||
QJsonObject jsonObjectProperties;
|
||||
QJsonArray jsonArrayProperties;
|
||||
if (!data.properties.value(QStringLiteral("Sequences")).toArray().isEmpty())
|
||||
jsonArrayProperties = data.properties.value(QStringLiteral("Sequences")).toArray();
|
||||
else
|
||||
jsonObjectProperties = data.properties;
|
||||
|
||||
secondColumnText += QStringLiteral("<br>- ") + tr("Description");
|
||||
thirdColumnText += QStringLiteral("<br>%1").arg(data.description);
|
||||
|
||||
if (jsonArrayProperties.isEmpty())
|
||||
{
|
||||
if (jsonObjectProperties.value(QStringLiteral("start")).toDouble() > 0)
|
||||
{
|
||||
secondColumnText += QStringLiteral("<br>- ") + tr("Start");
|
||||
thirdColumnText += QStringLiteral("<br>%1").arg(LtsTimeline::DateTime::toString(jsonObjectProperties.value(QStringLiteral("start")).toDouble() * 1000, LtsTimeline::DateTime::DTG));
|
||||
}
|
||||
if (data.duration > 0)
|
||||
{
|
||||
secondColumnText += QStringLiteral("<br>- ") + tr("Duration");
|
||||
thirdColumnText += QStringLiteral("<br>%1").arg(QTime::fromMSecsSinceStartOfDay(data.duration * 1000).toString());
|
||||
}
|
||||
if (jsonObjectProperties.value(QStringLiteral("speed")).toDouble() > 0)
|
||||
{
|
||||
secondColumnText += QStringLiteral("<br>- ") + tr("Speed");
|
||||
thirdColumnText += QStringLiteral("<br>%1").arg(jsonObjectProperties.value(QStringLiteral("speed")).toDouble());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jsonArrayProperties.first().toObject().value(QStringLiteral("start")).toDouble() > 0)
|
||||
{
|
||||
secondColumnText += QStringLiteral("<br>- ") + tr("Start");
|
||||
thirdColumnText += QStringLiteral("<br>%1").arg(LtsTimeline::DateTime::toString(jsonArrayProperties.first().toObject().value(QStringLiteral("start")).toDouble() * 1000, LtsTimeline::DateTime::DTG));
|
||||
}
|
||||
if (jsonArrayProperties.first().toObject().value(QStringLiteral("start")).toDouble() > 0)
|
||||
{
|
||||
secondColumnText += QStringLiteral("<br>- ") + tr("End");
|
||||
thirdColumnText += QStringLiteral("<br>%1").arg(LtsTimeline::DateTime::toString(jsonArrayProperties.last().toObject().value(QStringLiteral("start")).toDouble() * 1000 + jsonArrayProperties.last().toObject().value(QStringLiteral("duration")).toDouble() * 1000 ,LtsTimeline::DateTime::DTG ));
|
||||
}
|
||||
if (data.duration > 0)
|
||||
{
|
||||
secondColumnText += QStringLiteral("<br>- ") + tr("Duration");
|
||||
thirdColumnText += QStringLiteral("<br>%1").arg(QTime::fromMSecsSinceStartOfDay(data.duration * 1000).toString());
|
||||
}
|
||||
if (jsonArrayProperties.size() > 1)
|
||||
{
|
||||
QJsonValue tempProperty;
|
||||
for(QJsonValue property : jsonArrayProperties)
|
||||
{
|
||||
if (tempProperty.toObject().value(QStringLiteral("speed")).isNull())
|
||||
tempProperty = property;
|
||||
else
|
||||
if (tempProperty != property)
|
||||
{
|
||||
secondColumnText += QStringLiteral("<br>- ") + tr("Speed");
|
||||
thirdColumnText += QStringLiteral("<br>") + tr("variable");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
secondColumnText += QStringLiteral("<br>- ") + tr("Speed");
|
||||
thirdColumnText += QStringLiteral("<br>%1").arg(jsonArrayProperties.first().toObject().value(QStringLiteral("speed")).toDouble());
|
||||
}
|
||||
}
|
||||
m_ui->tableWidget->insertRow(topicCounter-1);
|
||||
|
||||
QTextEdit *firstColumnItem = new QTextEdit();
|
||||
firstColumnItem->setReadOnly(true);
|
||||
firstColumnItem->setStyleSheet(QStringLiteral("border : 0"));
|
||||
firstColumnItem->setWordWrapMode(QTextOption::NoWrap);
|
||||
firstColumnItem->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
firstColumnItem->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
firstColumnItem->setText(firstColumnText);
|
||||
firstColumnItem->setAlignment(Qt::AlignTop);
|
||||
m_ui->tableWidget->setCellWidget(topicCounter-1, 0, firstColumnItem);
|
||||
|
||||
QTextEdit *secondColumnItem = new QTextEdit();
|
||||
secondColumnItem->setReadOnly(true);
|
||||
secondColumnItem->setStyleSheet(QStringLiteral("border : 0"));
|
||||
secondColumnItem->setWordWrapMode(QTextOption::NoWrap);
|
||||
secondColumnItem->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
secondColumnItem->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
secondColumnItem->setText(secondColumnText);
|
||||
secondColumnItem->setAlignment(Qt::AlignTop);
|
||||
m_ui->tableWidget->setCellWidget(topicCounter-1, 1, secondColumnItem);
|
||||
|
||||
QTextEdit *thirdColumnItem = new QTextEdit;
|
||||
|
||||
thirdColumnItem->setReadOnly(true);
|
||||
thirdColumnItem->setStyleSheet(QStringLiteral("border : 0"));
|
||||
thirdColumnItem->setWordWrapMode(QTextOption::NoWrap);
|
||||
thirdColumnItem->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
thirdColumnItem->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
thirdColumnItem->setText(thirdColumnText);
|
||||
thirdColumnItem->setAlignment(Qt::AlignTop);
|
||||
m_ui->tableWidget->setCellWidget(topicCounter-1, 2, thirdColumnItem);
|
||||
|
||||
++topicCounter;
|
||||
}
|
||||
m_ui->tableWidget->resizeRowsToContents();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC SLOTS
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @details Print Storyboard with own printing Method
|
||||
*/
|
||||
void StoryboardDialog::onPrint()
|
||||
{
|
||||
QString headingHtml = (QStringLiteral("<H1>"));
|
||||
headingHtml.append (tr("Scriptname:"));
|
||||
headingHtml.append(m_scriptName);
|
||||
headingHtml.append(QStringLiteral("</H1>"));
|
||||
headingHtml.append(QStringLiteral("<H3>"));
|
||||
headingHtml.append(tr("Script Owner: "));
|
||||
headingHtml.append(m_scriptOwner);
|
||||
headingHtml.append(QStringLiteral("</H3>"));
|
||||
headingHtml.append(QStringLiteral("<H3>"));
|
||||
headingHtml.append(tr("Last modified: "));
|
||||
headingHtml.append(DTG::toStringMSec(m_lastModified));
|
||||
headingHtml.append(QStringLiteral("<br>"));
|
||||
headingHtml.append(tr("Duration: "));
|
||||
headingHtml.append(m_maxTopicTimeString);
|
||||
headingHtml.append(QStringLiteral("</H3><br>"));
|
||||
LtsPrinting::printStoryboard(m_session, this, m_ui->tableWidget, headingHtml);
|
||||
}
|
||||
|
||||
void StoryboardDialog::onColumnWidthChanged()
|
||||
{
|
||||
m_ui->tableWidget->setColumnWidth(0,this->width() * 0.25);
|
||||
m_ui->tableWidget->setColumnWidth(1,this->width() * 0.40);
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <LtsScriptTimeline/track_entry.h>
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsTimeline/item.h>
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
#include "pre_topic_item.h"
|
||||
|
||||
#include <QTableWidget>
|
||||
#include <QVector>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class StoryboardDialog;
|
||||
}
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class TopicItem;
|
||||
|
||||
|
||||
class StoryboardDialog : public LtsWidgets::LtsDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StoryboardDialog(
|
||||
QWidget *parent, LtsSession::LtsSession *session, const QString &scriptName, const QString &scriptOwner,
|
||||
const QString &maxTopicTimeString, qint64 lastModified, std::vector<TopicItem> topicItems
|
||||
);
|
||||
~StoryboardDialog();
|
||||
|
||||
void retranslate() override final;
|
||||
|
||||
void setScriptData(
|
||||
const QString &scriptName, const QString &scriptOwner, const QString &maxTopicTimeString, qint64 lastModified
|
||||
);
|
||||
void fillStoryboard(std::vector<TopicItem> topicItems);
|
||||
|
||||
public slots:
|
||||
void onPrint();
|
||||
void onColumnWidthChanged();
|
||||
private:
|
||||
LtsSession::LtsSession *m_session;
|
||||
|
||||
std::vector<TopicItem> m_topicItems;
|
||||
QString m_scriptName;
|
||||
QString m_scriptOwner;
|
||||
QString m_maxTopicTimeString;
|
||||
qint64 m_lastModified { 0 };
|
||||
bool m_isAlreadyTranslated { false };
|
||||
|
||||
Ui::StoryboardDialog *m_ui { nullptr };
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>StoryboardDialog</class>
|
||||
<widget class="QWidget" name="StoryboardDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>707</width>
|
||||
<height>464</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Script Editor</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0">
|
||||
<property name="spacing">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustIgnored</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textElideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="rowCount">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderMinimumSectionSize">
|
||||
<number>100</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>120</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderMinimumSectionSize">
|
||||
<number>0</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>150</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column/>
|
||||
<column/>
|
||||
<column/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Dialog for the Topic Designer
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-06-09
|
||||
*/
|
||||
|
||||
#include "pre_topic_designer_dialog.h"
|
||||
#include "ui_pre_topic_designer_dialog.h"
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
TopicDesignerDialog::TopicDesignerDialog(QWidget *parent)
|
||||
: LtsDialog(parent)
|
||||
, m_ui(new Ui::TopicDesignerDialog)
|
||||
{
|
||||
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Designer"));
|
||||
m_ui->setupUi(this);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
connect(m_ui->widget, &TopicDesigner::signalTopicSelected,
|
||||
this, &TopicDesignerDialog::signalTopicSelected);
|
||||
connect(m_ui->widget, &TopicDesigner::signalDeleteTrackEntry,
|
||||
this, &TopicDesignerDialog::signalDeleteTrackEntry);
|
||||
connect(m_ui->widget, &TopicDesigner::signalUnselect,
|
||||
this, &TopicDesignerDialog::signalUnselect);
|
||||
connect(m_ui->widget, &TopicDesigner::signalChangesMade,
|
||||
this, &LtsWidgets::LtsDialog::signalChangesMade);
|
||||
|
||||
// connect(m_ui->pbToFront, &QPushButton::clicked,
|
||||
// m_ui->widget, &TopicDesigner::to)
|
||||
}
|
||||
|
||||
|
||||
void TopicDesignerDialog::retranslate()
|
||||
{
|
||||
translate(m_ui, tr("Topic Designer"));
|
||||
}
|
||||
|
||||
|
||||
TopicDesignerDialog::~TopicDesignerDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
|
||||
QVector<LtsScriptTimeline::TrackEntry *> TopicDesignerDialog::exportTopicList() const
|
||||
{
|
||||
return m_ui->widget->exportTopicList();
|
||||
}
|
||||
|
||||
|
||||
void TopicDesignerDialog::selectTopic(int topicId)
|
||||
{
|
||||
m_ui->widget->selectTopic(topicId);
|
||||
}
|
||||
|
||||
|
||||
void TopicDesignerDialog::loadTopicList(const QVector<LtsScriptTimeline::TrackEntry *> &topicsList)
|
||||
{
|
||||
m_ui->widget->loadTopicList(topicsList);
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Dialog for the Topic Designer
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-06-09
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
|
||||
#include <QVector>
|
||||
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class TopicDesignerDialog;
|
||||
}
|
||||
|
||||
namespace LtsScriptTimeline
|
||||
{
|
||||
class TrackEntry;
|
||||
}
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class TopicDesignerDialog : public LtsWidgets::LtsDialog
|
||||
{
|
||||
public:
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TopicDesignerDialog(QWidget *parent = nullptr);
|
||||
~TopicDesignerDialog();
|
||||
|
||||
void loadTopicList(const QVector<LtsScriptTimeline::TrackEntry *> &topicsList);
|
||||
QVector<LtsScriptTimeline::TrackEntry *> exportTopicList() const;
|
||||
void selectTopic(int topicId);
|
||||
|
||||
signals:
|
||||
void signalTopicSelected(int topicId);
|
||||
void signalDeleteTrackEntry(int topicId);
|
||||
void signalUnselect();
|
||||
|
||||
private:
|
||||
void retranslate() override;
|
||||
|
||||
Ui::TopicDesignerDialog *m_ui;
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
Executable
+122
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TopicDesignerDialog</class>
|
||||
<widget class="QWidget" name="TopicDesignerDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>338</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Script Editor</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string notr="true">Some text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbToFront">
|
||||
<property name="text">
|
||||
<string>To Front</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbIncIdx">
|
||||
<property name="text">
|
||||
<string>Inc Idx</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbDecIdx">
|
||||
<property name="text">
|
||||
<string>Dec Idx</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbToBack">
|
||||
<property name="text">
|
||||
<string>To Back</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="PresentationManagement::TopicDesigner" name="widget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PresentationManagement::TopicDesigner</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>topic_designer.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
#include "pre_topic_item.h"
|
||||
#include "pre_script_model.h"
|
||||
|
||||
#include <PresentationManagement/pre.hpp>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QPainter>
|
||||
#include <QStyleOptionViewItem>
|
||||
|
||||
|
||||
#include <LtsTimeline/timeline_utils.h>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
int TopicItem::s_gradientSize = 20;
|
||||
|
||||
const QString TopicItem::s_sequencesKey = QStringLiteral("Sequences");
|
||||
const QString TopicItem::s_posKey = QStringLiteral("pos");
|
||||
const QString TopicItem::s_startKey = QStringLiteral("start");
|
||||
const QString TopicItem::s_durationKey = QStringLiteral("duration");
|
||||
const QString TopicItem::s_speedKey = QStringLiteral("speed");
|
||||
|
||||
|
||||
TopicItem::TopicItem(const TopicEditorBase::TopicData &data)
|
||||
: LtsTimeline::Item(static_cast<qint64>(data.startTime) * 1000, static_cast<qint64>(data.duration) * 1000, topicTypeColor(data.topicType), true)
|
||||
, m_topicType(data.topicType)
|
||||
, m_topicData(data)
|
||||
{
|
||||
sortSequences();
|
||||
}
|
||||
|
||||
TopicItem::TopicItem(const PresentationContent &content)
|
||||
: LtsTimeline::Item(static_cast<qint64>(content.startTime()) * 1000, static_cast<qint64>(content.duration()) * 1000, TopicEditorBase::topicTypeColor(TopicEditorBase::topicTypeFromString(QString::fromStdString(content.presentationTopicType()))), true)
|
||||
, m_topicType(TopicEditorBase::topicTypeFromString(QString::fromStdString(content.presentationTopicType())))
|
||||
{
|
||||
m_topicData.id = content.id();
|
||||
m_topicData.topicType = m_topicType;
|
||||
m_topicData.startTime = m_startTime / 1000;
|
||||
m_topicData.duration = m_duration / 1000;
|
||||
m_topicData.state = true;
|
||||
m_topicData.index = content.index();
|
||||
m_topicData.description = QString::fromStdString(content.description());
|
||||
m_topicData.channel = QString::fromStdString(content.channel());
|
||||
m_topicData.properties = QJsonDocument::fromJson(QByteArray(content.attributes().c_str(), content.attributes().size())).object();
|
||||
m_topicData.x = content.x();
|
||||
m_topicData.y = content.y();
|
||||
m_topicData.w = content.width();
|
||||
m_topicData.h = content.height();
|
||||
sortSequences();
|
||||
}
|
||||
|
||||
TopicItem::TopicItem(
|
||||
qint64 startTime, int duration, TopicEditorBase::TopicType topicType, const QString &description,
|
||||
const QJsonObject &properties, const QString &channel, const QString &parentChannel
|
||||
)
|
||||
: LtsTimeline::Item(startTime, duration, topicTypeColor(topicType))
|
||||
, m_topicType(topicType)
|
||||
{
|
||||
m_topicData.topicType = m_topicType;
|
||||
m_topicData.startTime = m_startTime / 1000;
|
||||
m_topicData.duration = m_duration / 1000;
|
||||
m_topicData.description = description;
|
||||
m_topicData.channel = channel;
|
||||
m_topicData.parentChannel = parentChannel;
|
||||
m_topicData.properties = properties;
|
||||
sortSequences();
|
||||
}
|
||||
|
||||
TopicItem::~TopicItem()
|
||||
{
|
||||
delete m_replayInterface;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC
|
||||
|
||||
TopicItem TopicItem::copy() const
|
||||
{
|
||||
return TopicItem(m_topicData);
|
||||
}
|
||||
|
||||
|
||||
qint64 TopicItem::replayTime(qint64 time) const
|
||||
{
|
||||
if (m_startTime > time || time > m_startTime + m_duration)
|
||||
return -1;
|
||||
|
||||
if (ScriptModel::hasReplayTime(m_topicType))
|
||||
{
|
||||
if (m_topicData.properties.contains(s_sequencesKey))
|
||||
{
|
||||
QJsonArray sequences = m_topicData.properties.value(s_sequencesKey).toArray({});
|
||||
if (sequences.isEmpty())
|
||||
return -1;
|
||||
|
||||
qint64 relTime = time - m_startTime;
|
||||
qint64 relSequenceStart = 0;
|
||||
for (const QJsonValue &sequence : qAsConst(sequences))
|
||||
{
|
||||
QJsonObject obj = sequence.toObject();
|
||||
qint64 start = obj.value(s_startKey).toDouble();
|
||||
double speed = obj.value(s_speedKey).toDouble() / 100.;
|
||||
int duration = speed * obj.value(s_durationKey).toInt();
|
||||
if (relTime <= relSequenceStart + speed * duration)
|
||||
return start + speed * (relTime - relSequenceStart);
|
||||
|
||||
relSequenceStart += speed * duration;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qint64 start = static_cast<qint64>(m_topicData.properties.value(QStringLiteral("start")).toDouble(-1));
|
||||
if (start < 0)
|
||||
return -1;
|
||||
|
||||
qint64 end = static_cast<qint64>(m_topicData.properties.value(QStringLiteral("end")).toDouble(-1));
|
||||
if (end < 0)
|
||||
return -1;
|
||||
|
||||
return start + (end - start) * (time - m_startTime) / m_duration;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
QJsonObject TopicItem::toJson() const
|
||||
{
|
||||
return QJsonObject({{QStringLiteral("topicId"), id()},
|
||||
{QStringLiteral("startTime"), m_startTime / 1000},
|
||||
{QStringLiteral("length"), m_duration / 1000},
|
||||
{QStringLiteral("type"), static_cast<int>(m_topicType)},
|
||||
{QStringLiteral("x"), m_topicData.x},
|
||||
{QStringLiteral("y"), m_topicData.y},
|
||||
{QStringLiteral("w"), m_topicData.w},
|
||||
{QStringLiteral("h"), m_topicData.h},
|
||||
{QStringLiteral("detailData"), m_topicData.properties},
|
||||
{QStringLiteral("channelName"), m_topicData.channel},
|
||||
{QStringLiteral("description"), m_topicData.description},
|
||||
{QStringLiteral("filterParentChannel"), m_topicData.parentChannel}
|
||||
});
|
||||
}
|
||||
|
||||
PresentationContent TopicItem::toPresentationContent(int scriptId) const
|
||||
{
|
||||
PresentationManagement::PresentationContent content;
|
||||
content.id(id());
|
||||
content.presentationId(scriptId);
|
||||
content.presentationTopicType(topicTypeString(m_topicType).toStdString());
|
||||
content.channel(m_topicData.channel.toStdString());
|
||||
content.startTime(m_startTime / 1000);
|
||||
content.duration(m_duration / 1000);
|
||||
const QByteArray attributes = QJsonDocument(m_topicData.properties).toJson(QJsonDocument::Compact);
|
||||
content.attributes(attributes.data());
|
||||
content.description(m_topicData.description.toStdString());
|
||||
content.x(m_topicData.x);
|
||||
content.y(m_topicData.y);
|
||||
content.height(m_topicData.h);
|
||||
content.width(m_topicData.w);
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
void TopicItem::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, bool highlight) const
|
||||
{
|
||||
painter->save();
|
||||
|
||||
LtsTimeline::Item::paint(painter, option, index, highlight);
|
||||
if (option.rect.width() >= 20)
|
||||
{
|
||||
QRect boundingRect = option.rect.adjusted(1, 1, -1, -1);
|
||||
QRect labelRect = boundingRect.adjusted(3, 16, 0, 0);
|
||||
painter->drawText(labelRect, Qt::AlignLeft, description(), &boundingRect);
|
||||
|
||||
int penWidth = highlight ? 5 : 1;
|
||||
QRect gradientRect = QRect(
|
||||
option.rect.right() - s_gradientSize, option.rect.top() + penWidth, s_gradientSize, option.rect.height() - 2 * penWidth
|
||||
);
|
||||
QLinearGradient gradient(gradientRect.bottomRight(), gradientRect.bottomLeft());
|
||||
gradient.setColorAt(0, QColor(m_color.red(), m_color.green(), m_color.blue(), 255));
|
||||
gradient.setColorAt(1, QColor(m_color.red(), m_color.green(), m_color.blue(), 0));
|
||||
painter->fillRect(gradientRect, gradient);
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE
|
||||
|
||||
void TopicItem::sortSequences()
|
||||
{
|
||||
if (!m_topicData.properties.contains(s_sequencesKey))
|
||||
return;
|
||||
|
||||
QJsonArray sequences = m_topicData.properties.value(s_sequencesKey).toArray({});
|
||||
if (sequences.isEmpty())
|
||||
return;
|
||||
|
||||
QVariantList variantList = sequences.toVariantList();
|
||||
std::sort(variantList.begin(), variantList.end(), [](const QVariant &left, const QVariant &right)
|
||||
{
|
||||
return left.toJsonObject().value(s_posKey).toInt() <= right.toJsonObject().value(s_posKey).toInt();
|
||||
});
|
||||
m_topicData.properties[s_sequencesKey] = QJsonValue::fromVariant(variantList);
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include "pre_utils.h"
|
||||
|
||||
#include <LtsScriptTimeline/track_entry.h>
|
||||
#include <LtsTimeline/item.h>
|
||||
#include <PluginInterfaces/topic_replay_interface.h>
|
||||
#include <TopicEditorBase/topic_editor_utils.h>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class PresentationContent;
|
||||
|
||||
|
||||
class TopicItem : public LtsTimeline::Item
|
||||
{
|
||||
friend class ScriptModel;
|
||||
|
||||
public:
|
||||
static int s_gradientSize;
|
||||
|
||||
static const QString s_sequencesKey;
|
||||
static const QString s_posKey;
|
||||
static const QString s_startKey;
|
||||
static const QString s_durationKey;
|
||||
static const QString s_speedKey;
|
||||
|
||||
explicit TopicItem(const TopicEditorBase::TopicData &data);
|
||||
explicit TopicItem(const PresentationContent &content);
|
||||
TopicItem(TopicItem &&) = default;
|
||||
explicit TopicItem(
|
||||
qint64 startTime, int duration, TopicEditorBase::TopicType topicType, const QString &description,
|
||||
const QJsonObject &properties, const QString &channel, const QString &parentChannel = {}
|
||||
);
|
||||
~TopicItem();
|
||||
|
||||
TopicItem copy() const;
|
||||
|
||||
TopicReplay::Interface *replayInterface() { return m_replayInterface; }
|
||||
void setReplayInterface(TopicReplay::Interface *replayInterface) { m_replayInterface = replayInterface; }
|
||||
bool hasReplayInterface() const { return bool(m_replayInterface); }
|
||||
|
||||
QWidget *replayWidget() { return m_replayWidget; }
|
||||
void setReplayWidget(QWidget *widget) { m_replayWidget = widget; }
|
||||
bool hasReplayWidget() const { return bool(m_replayWidget); }
|
||||
|
||||
TopicEditorBase::TopicType topicType() const { return m_topicType; }
|
||||
TopicEditorBase::TopicData &topicData() { return m_topicData; }
|
||||
const TopicEditorBase::TopicData &topicData() const { return m_topicData; }
|
||||
|
||||
qint64 replayTime(qint64 time) const;
|
||||
|
||||
int topicId() const { return m_topicData.id; }
|
||||
void setTopicId(int id) { m_topicData.id = id; }
|
||||
|
||||
QString description() const { return m_topicData.description; }
|
||||
void setDescription(const QString &description) { m_topicData.description = description; }
|
||||
|
||||
QString channel() const { return m_topicData.channel; }
|
||||
void setChannel(const QString &channelName) { m_topicData.channel = channelName; }
|
||||
QString parentChannel() const { return m_topicData.parentChannel; }
|
||||
void setParentChannel(const QString &channelName) { m_topicData.parentChannel = channelName; }
|
||||
|
||||
QJsonObject properties() const { return m_topicData.properties; }
|
||||
void setProperties(const QJsonObject &properties) { m_topicData.properties = properties; }
|
||||
|
||||
bool state() const { return m_topicData.state; }
|
||||
void setState(bool state) { m_topicData.state = state; }
|
||||
|
||||
QJsonObject toJson() const;
|
||||
PresentationContent toPresentationContent(int scriptId = -1) const;
|
||||
|
||||
void paint(
|
||||
QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, bool highlight = false
|
||||
) const override;
|
||||
|
||||
private:
|
||||
void sortSequences();
|
||||
|
||||
private:
|
||||
TopicReplay::Interface *m_replayInterface { nullptr }; // NOTE: because of this raw pointer, we can't follow the rule-of-zero here - but because the pointer address just needs to be copied, defaulted move constructor is enough
|
||||
QWidget *m_replayWidget { nullptr };
|
||||
|
||||
TopicEditorBase::TopicType m_topicType;
|
||||
TopicEditorBase::TopicData m_topicData;
|
||||
|
||||
};
|
||||
typedef std::unique_ptr<TopicItem> TopicItemPtr;
|
||||
typedef std::vector<TopicItemPtr> TopicItems;
|
||||
typedef std::reference_wrapper<const TopicItem /* TODO: make Item, also! */> TopicItemPtrRef;
|
||||
//typedef std::reference_wrapper<TopicItem> TopicItemPtrNCVRef;
|
||||
typedef std::reference_wrapper<TopicItemPtr> TopicItemPtrNCVRef;
|
||||
typedef std::vector<TopicItemPtrNCVRef> TopicItemPtrNCVRefs;
|
||||
typedef std::vector<TopicItemPtrRef> TopicItemPtrRefs;
|
||||
|
||||
} // namespace PresentationManagement
|
||||
Executable
+308
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* class for recording a single topic on a screen
|
||||
* $Id:
|
||||
* $HeadURL:
|
||||
* $Author:
|
||||
* Created by Hannes Fritsche, SLTE, 2023-11-06
|
||||
*/
|
||||
|
||||
#include "pre_topic_recorder.h"
|
||||
#include "pre_screen.h"
|
||||
|
||||
#include <LtsUtilsCore/await.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QThread>
|
||||
|
||||
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#define _compiler_warning(x) _Pragma(#x)
|
||||
#define compiler_warning(x) _compiler_warning(GCC warning x)
|
||||
#else
|
||||
#define compiler_warning(x)
|
||||
#endif
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
static QString s_container(L("mp4"));
|
||||
static QString s_program(L("ffmpeg"));
|
||||
|
||||
TopicRecorder::TopicRecorder(const int topicId, const QString &topicName, const Screen *screen, qint32 offset,
|
||||
qint64 startTime, qint64 stopTime, const double topicX, const double topicY,
|
||||
const double topicW, const double topicH, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_recordingProcess(new QProcess(this))
|
||||
, m_startTime(startTime)
|
||||
, m_stopTime(stopTime)
|
||||
, m_topicName(topicName)
|
||||
, m_topicId(topicId)
|
||||
, m_length((m_stopTime - (m_startTime - offset)) / 1000)
|
||||
, m_logger(getLogger("TopicRecorder"))
|
||||
{
|
||||
const auto screenGlobalPos = screen->mapToGlobal({0,0});
|
||||
const auto screenGeo = screen->geometry();
|
||||
|
||||
const double screenX = screenGlobalPos.x() + screenGeo.x();
|
||||
const double screenY = screenGlobalPos.y() + screenGeo.y();
|
||||
const double screenW = screenGeo.width();
|
||||
const double screenH = screenGeo.height();
|
||||
|
||||
const bool fullscreen = (topicW < 0.0002 || topicH < 0.0002); // if w or h are too small, record the whole screen...
|
||||
|
||||
const QPoint globalPos(fullscreen ? screenX : screenX + (screenW * topicX),
|
||||
fullscreen ? screenY : screenY + (screenH * topicY));
|
||||
|
||||
const qint32 inputX = globalPos.x();
|
||||
const qint32 inputY = globalPos.y();
|
||||
const qint32 inputW = (qint32)(fullscreen ? screenW : screenW * topicW);
|
||||
const qint32 inputH = (qint32)(fullscreen ? screenH : screenH * topicH);
|
||||
|
||||
ERROR_LOG(m_logger) << "TopicRecorder: fullscreen? " << fullscreen;
|
||||
ERROR_LOG(m_logger) << "TopicRecorder: topic - x: " << topicX << ", y: " << topicY << ", w: " << topicW << ", h: " << topicH;
|
||||
ERROR_LOG(m_logger) << "TopicRecorder: screen - x: " << screenX << ", y: " << screenY << ", w: " << screenW << ", h: " << screenH;
|
||||
ERROR_LOG(m_logger) << "TopicRecorder: input - x: " << inputX << ", y: " << inputY << ", w: " << inputW << ", h: " << inputH;
|
||||
|
||||
if (!m_tmpRecordingFile.open()) // create file
|
||||
throw std::runtime_error(L("Failed to open temporary recording file: %1").arg(m_tmpRecordingFile.errorString()).toStdString());
|
||||
|
||||
m_recordingProcess->setProgram(s_program);
|
||||
m_recordingProcess->setArguments(L("-hide_banner -f x11grab -s %1x%2 -i :0.0+%3,%4 -c:v libx264 -y -map 0 -movflags +faststart -f %5 %6").arg(inputW).arg(inputH).arg(inputX).arg(inputY).arg(s_container).arg(m_tmpRecordingFile.fileName()).split(L(" ")));
|
||||
/*DEBUG*/ERROR_LOG(m_logger) << "args: " << m_recordingProcess->arguments().join(L(" "));
|
||||
connect(m_recordingProcess, &QProcess::readyReadStandardOutput, [this]() { /*DEBUG*/ERROR_LOG(m_logger) << "REC [ ]: " << QString::fromUtf8(m_recordingProcess->readAllStandardOutput()); });
|
||||
connect(m_recordingProcess, &QProcess::readyReadStandardError, [this]() { /*DEBUG*/ERROR_LOG(m_logger) << "REC [!]: " << QString::fromUtf8(m_recordingProcess->readAllStandardError()); });
|
||||
connect(m_recordingProcess, &QProcess::started, [this]() { /*DEBUG*/ERROR_LOG(m_logger) << "REC started successfully!"; });
|
||||
|
||||
if (!m_tmpCuttingFile.open()) // create file
|
||||
throw std::runtime_error(L("Failed to open temporary cutting file: %1").arg(m_tmpCuttingFile.errorString()).toStdString());
|
||||
|
||||
connect(m_recordingProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this, &TopicRecorder::onRecordingProcessFinished);
|
||||
connect(m_recordingProcess, &QProcess::errorOccurred,
|
||||
this, QOverload<QProcess::ProcessError>::of(&TopicRecorder::onExportFailed));
|
||||
}
|
||||
|
||||
TopicRecorder::~TopicRecorder()
|
||||
{
|
||||
if (m_recordingProcess->state() != QProcess::NotRunning)
|
||||
m_recordingProcess->kill();
|
||||
//m_recordingProcess->waitForFinished();
|
||||
|
||||
// if (m_cuttingProcess->state() == QProcess::Running)
|
||||
// m_cuttingProcess->terminate();
|
||||
// m_cuttingProcess->waitForFinished();
|
||||
}
|
||||
|
||||
|
||||
void TopicRecorder::cut()
|
||||
{
|
||||
auto *cuttingProcess = new QProcess(this);
|
||||
|
||||
cuttingProcess->setProgram(s_program);
|
||||
// cut with only 1 thread: cuttingProcess->setArguments(L("-threads 1 -hide_banner -y -sseof -%1 -threads 1 -i %2 -c:v libx264 -x264-params threads=1 -f %3 %4").arg(m_length).arg(m_tmpRecordingFile.fileName(), s_container, m_tmpCuttingFile.fileName()).split(L(" ")));
|
||||
cuttingProcess->setArguments(L("-hide_banner -y -sseof -%1 -i %2 -c:v libx264 -f %3 %4").arg(m_length).arg(m_tmpRecordingFile.fileName(), s_container, m_tmpCuttingFile.fileName()).split(L(" ")));
|
||||
connect(cuttingProcess, &QProcess::readyReadStandardOutput, this, [this, cuttingProcess]() { /*DEBUG*/ERROR_LOG(m_logger) << "CUT [ ]: " << QString::fromUtf8(cuttingProcess->readAllStandardOutput()); });
|
||||
connect(cuttingProcess, &QProcess::readyReadStandardError, this, [this, cuttingProcess]() { /*DEBUG*/ERROR_LOG(m_logger) << "CUT [!]: " << QString::fromUtf8(cuttingProcess->readAllStandardError()); });
|
||||
connect(cuttingProcess, &QProcess::started, this, [this]() { /*DEBUG*/ERROR_LOG(m_logger) << "CUT started successfully!"; });
|
||||
|
||||
ERROR_LOG(m_logger) << "start cut";
|
||||
|
||||
cuttingProcess->start();
|
||||
|
||||
// if (m_recordingProcess->state() == QProcess::NotRunning)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
try
|
||||
{
|
||||
LtsUtils::await(
|
||||
cuttingProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), true,
|
||||
cuttingProcess, &QProcess::errorOccurred, false,
|
||||
this, &TopicRecorder::cancel, false
|
||||
);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "cut failed (error) with exitCode=" << cuttingProcess->exitCode() << " and exitStatus=" << (cuttingProcess->exitStatus() == QProcess::ExitStatus::CrashExit ? "Crash" : "Normal Exit");
|
||||
m_failed = true;
|
||||
cuttingProcess->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cuttingProcess->exitCode() != 0)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "cut failed with non-zero exitCode=" << cuttingProcess->exitCode() << " and exitStatus=" << (cuttingProcess->exitStatus() == QProcess::ExitStatus::CrashExit ? "Crash" : "Normal Exit");
|
||||
m_failed = true;
|
||||
cuttingProcess->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ERROR_LOG(m_logger) << "cut done";
|
||||
cuttingProcess->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
void TopicRecorder::save(const QString &saveToPath) noexcept(false)
|
||||
{
|
||||
const QString newFilename(L("%1/%2.%3").arg(saveToPath).arg(m_topicId).arg(s_container));
|
||||
if (!m_tmpCuttingFile.rename(newFilename))
|
||||
{
|
||||
throw L("Failed to move recording from '%1' to '%2' ('%3')").arg(m_tmpCuttingFile.fileName(), saveToPath, newFilename);
|
||||
}
|
||||
|
||||
m_tmpCuttingFile.setAutoRemove(false); // keep file
|
||||
}
|
||||
|
||||
|
||||
//void TopicRecorder::saveRecording(const QString &saveToPath)
|
||||
//{
|
||||
// const QString newFilename(L("%1/%2.%3").arg(saveToPath).arg(m_topicId).arg(s_container));
|
||||
// if (!m_tmpCuttingFile.rename(newFilename))
|
||||
// {
|
||||
// throw L("Failed to move recording from '%1' to '%2' ('%3')").arg(m_tmpCuttingFile.fileName(), saveToPath, newFilename);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// m_tmpCuttingFile.setAutoRemove(false); // keep file
|
||||
//}
|
||||
|
||||
|
||||
void TopicRecorder::onExportFailed(const QProcess::ProcessError error)
|
||||
{
|
||||
m_failed = true;
|
||||
onExportFailed(L("Process: '%1' - Process error: %2").arg(processToString(static_cast<QProcess*>(sender()))).arg((int)error));
|
||||
}
|
||||
|
||||
|
||||
void TopicRecorder::onExportFailed(const QString &errorMessage)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Export failed for topic '" << m_topicName << "': " << errorMessage;
|
||||
emit exportFailed(errorMessage);
|
||||
}
|
||||
|
||||
|
||||
void TopicRecorder::killAndClear()
|
||||
{
|
||||
switch (m_recordingState)
|
||||
{
|
||||
case RecordingState::Done:
|
||||
// m_cuttingProcess->kill();
|
||||
|
||||
Q_FALLTHROUGH();
|
||||
case RecordingState::InProgress:
|
||||
m_recordingProcess->kill();
|
||||
|
||||
return;
|
||||
case RecordingState::Waiting:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TopicRecorder::waitForFinishedRecording()
|
||||
{
|
||||
if (m_recordingProcess->state() == QProcess::NotRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LtsUtils::await(
|
||||
m_recordingProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), true,
|
||||
m_recordingProcess, &QProcess::errorOccurred, false,
|
||||
this, &TopicRecorder::cancel, false
|
||||
);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "waitForFinishedRecording: catch!";
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << "waitForFinishedRecording: done waiting!";
|
||||
}
|
||||
|
||||
|
||||
void TopicRecorder::onNewTime(qint64 time)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "onNewTime: " << time << ", startTime: " << m_startTime;
|
||||
|
||||
switch (m_recordingState)
|
||||
{
|
||||
case RecordingState::Waiting:
|
||||
if (time < m_startTime)
|
||||
return;
|
||||
|
||||
ERROR_LOG(m_logger) << "onNewTime: started at " << time << " while startTime is " << m_startTime;
|
||||
|
||||
/*DEBUG*/ERROR_LOG(m_logger) << "Starting \"" << processToString(m_recordingProcess) << "\"";
|
||||
|
||||
m_recordingProcess->start();
|
||||
|
||||
m_recordingState = RecordingState::InProgress;
|
||||
|
||||
return;
|
||||
case RecordingState::InProgress:
|
||||
if (time < m_stopTime)
|
||||
return;
|
||||
|
||||
/*DEBUG*/ERROR_LOG(m_logger) << "Stopping recording for " << m_topicName;
|
||||
|
||||
m_recordingProcess->terminate();
|
||||
|
||||
m_recordingState = RecordingState::Done;
|
||||
|
||||
return;
|
||||
case RecordingState::Done:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TopicRecorder::onRecordingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
m_recordingState = RecordingState::Done;
|
||||
/*DEBUG*/ERROR_LOG(m_logger) << "Recording finished for " << m_topicName << ", with exitCode=" << exitCode << " and exitStatus=" << exitStatus;
|
||||
|
||||
// if (exitStatus != QProcess::ExitStatus::NormalExit)
|
||||
// {
|
||||
// onExportFailed(L("Recording process crashed!"));
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (exitCode != 0)
|
||||
// {
|
||||
// onExportFailed(L("Recording process exited with exitCode %1").arg(exitCode));
|
||||
// return;
|
||||
// }
|
||||
|
||||
// /*DEBUG*/ERROR_LOG(m_logger) << "Starting \"" << processToString(m_cuttingProcess) << "\"";
|
||||
|
||||
// m_cuttingProcess->start();
|
||||
}
|
||||
|
||||
|
||||
//void TopicRecorder::onCuttingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
//{
|
||||
// /*DEBUG*/ERROR_LOG(m_logger) << "Cutting finished for " << m_topicName << ", with exitCode=" << exitCode << " and exitStatus=" << exitStatus;
|
||||
|
||||
//// if (exitCode != 0)
|
||||
//// {
|
||||
//// onExportFailed(L("Cutting process exited with exitCode %1").arg(exitCode));
|
||||
//// return;
|
||||
//// }
|
||||
//}
|
||||
|
||||
|
||||
QString TopicRecorder::processToString(QProcess *process)
|
||||
{
|
||||
return L("%1 %2").arg(process->program(), process->arguments().join(L(" ")));
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* class for recording a single topic on a screen
|
||||
* $Id:
|
||||
* $HeadURL:
|
||||
* $Author:
|
||||
* Created by Hannes Fritsche, SLTE, 2023-11-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTemporaryFile>
|
||||
#include <QProcess>
|
||||
#include <Services/PresentationManagement/logger.h>
|
||||
|
||||
class Screen;
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class TopicItem;
|
||||
|
||||
class TopicRecorder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public: // methods
|
||||
TopicRecorder(const int topicId, const QString &topicName, const Screen *screen, qint32 offset,
|
||||
qint64 startTime, qint64 stopTime, const double topicX, const double topicY,
|
||||
const double topicW, const double topicH, QObject *parent = nullptr);
|
||||
~TopicRecorder();
|
||||
|
||||
|
||||
|
||||
|
||||
void cut();
|
||||
void save(const QString &saveToPath) noexcept(false);
|
||||
|
||||
|
||||
|
||||
void killAndClear();
|
||||
void waitForFinishedRecording();
|
||||
// void saveRecording(const QString &saveToPath);
|
||||
bool isFailed() const { return m_failed; }
|
||||
const QString &topicName() const { return m_topicName; }
|
||||
int topicId() const { return m_topicId; }
|
||||
|
||||
public Q_SLOTS:
|
||||
void onNewTime(qint64 time);
|
||||
|
||||
private: // other
|
||||
enum class RecordingState
|
||||
{
|
||||
Waiting,
|
||||
InProgress,
|
||||
Done
|
||||
};
|
||||
|
||||
signals:
|
||||
void exportFailed(const QString &errorMessage);
|
||||
void cancel();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onRecordingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
// void onCuttingProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void onExportFailed(const QProcess::ProcessError error);
|
||||
void onExportFailed(const QString &errorMessage);
|
||||
|
||||
private: // methods
|
||||
static QString processToString(QProcess *process);
|
||||
|
||||
private: // attributes
|
||||
QProcess *m_recordingProcess { nullptr };
|
||||
// QProcess *m_cuttingProcess { nullptr };
|
||||
|
||||
bool m_hasRecordingDone { false };
|
||||
bool m_isWorking { false };
|
||||
|
||||
const qint64 m_startTime;
|
||||
const qint64 m_stopTime;
|
||||
const QString m_topicName;
|
||||
QTemporaryFile m_tmpRecordingFile;
|
||||
QTemporaryFile m_tmpCuttingFile;
|
||||
RecordingState m_recordingState { RecordingState::Waiting };
|
||||
const int m_topicId;
|
||||
bool m_failed { false };
|
||||
qint32 m_length;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
}; // class TopicRecorder
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* class for recording a single topic on a screen
|
||||
* $Id:
|
||||
* $HeadURL:
|
||||
* $Author:
|
||||
* Created by Hannes Fritsche, SLTE, 2023-11-06
|
||||
*/
|
||||
|
||||
#include "pre_topic_shotter.h"
|
||||
#include "pre_screen.h"
|
||||
#include <QFile>
|
||||
#include <QThread>
|
||||
#include <QScreen>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
TopicShotter::TopicShotter(const int topicId, const QString &topicName, const Screen *screen, qint32 offset,
|
||||
qint64 startTime, qint64 stopTime, const double topicX, const double topicY,
|
||||
const double topicW, const double topicH, const bool isFilterTopic, const QString &desc, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_startTime(startTime)
|
||||
, m_topicName(topicName)
|
||||
, m_topicId(topicId)
|
||||
, m_screen(screen)
|
||||
, m_topicX(topicX)
|
||||
, m_topicY(topicY)
|
||||
, m_topicW(topicW)
|
||||
, m_topicH(topicH)
|
||||
, m_desc(desc)
|
||||
, m_isFilterTopic(isFilterTopic)
|
||||
, m_logger(getLogger("TopicShotter"))
|
||||
{}
|
||||
|
||||
|
||||
TopicShotter::~TopicShotter()
|
||||
{}
|
||||
|
||||
|
||||
void TopicShotter::saveRecording(const QString &saveToPath)
|
||||
{
|
||||
const QString newFilename(QStringLiteral("%1/%2.png").arg(saveToPath).arg(m_topicId));
|
||||
if (!m_tmpFile.rename(newFilename))
|
||||
{
|
||||
throw QStringLiteral("Failed to move screenshot from '%1' to '%2' ('%3')").arg(m_tmpFile.fileName(), saveToPath, newFilename);
|
||||
return;
|
||||
}
|
||||
|
||||
m_tmpFile.setAutoRemove(false); // keep file
|
||||
}
|
||||
|
||||
|
||||
void TopicShotter::onExportFailed(const QString &errorMessage)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Export failed for topic '" << m_topicName << "': " << errorMessage;
|
||||
emit exportFailed(errorMessage);
|
||||
}
|
||||
|
||||
|
||||
void TopicShotter::onNewTime(qint64 time)
|
||||
{
|
||||
// filter topics start a second later (loading issues), so start time needs to be adjusted for them:
|
||||
const qint64 filterTopicDelay = (m_isFilterTopic ? 1000 : 0);
|
||||
const qint64 startTime = m_startTime + filterTopicDelay + 1000 /* + 1000 because onNewTime is triggered *before* all the playTopic calls >_> */;
|
||||
|
||||
DEBUG_LOG(m_logger) << "onNewTime: " << time << ", startTime: " << startTime << ", isFilterTopic? " << m_isFilterTopic << ", desc: " << m_desc;
|
||||
|
||||
if (time < startTime)
|
||||
return;
|
||||
|
||||
if (isStarted)
|
||||
return;
|
||||
isStarted = true;
|
||||
|
||||
DEBUG_LOG(m_logger) << "onNewTime: started at " << time << " while startTime is " << startTime;
|
||||
|
||||
if (!m_tmpFile.open()) // create file
|
||||
{
|
||||
onExportFailed(QStringLiteral("Can't create temporary file for screenshot at %1: %2").arg(m_tmpFile.fileName(), m_tmpFile.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto geo = m_screen->geometry();
|
||||
const double screenW = geo.width();
|
||||
const double screenH = geo.height();
|
||||
const qint32 inputX = geo.x() + (screenW * m_topicX);
|
||||
const qint32 inputY = geo.y() + (screenH * m_topicY);
|
||||
const qint32 inputW = (qint32)(screenW * m_topicW);
|
||||
const qint32 inputH = (qint32)(screenH * m_topicH);
|
||||
|
||||
QScreen *_screen = m_screen->window()->screen();
|
||||
if (!_screen)
|
||||
{
|
||||
onExportFailed(QStringLiteral("Can't get system window of screen!"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// defered start of grabWindow as the to-be-shotted widget needs to initialize first
|
||||
auto *t = new QTimer(this);
|
||||
t->setInterval(750);
|
||||
t->setSingleShot(true);
|
||||
t->start();
|
||||
|
||||
connect(t, &QTimer::timeout, this, [this, inputX, inputY, inputW, inputH, _screen]() // =>
|
||||
{
|
||||
const WId winId = m_screen->winId();
|
||||
DEBUG_LOG(m_logger) << "## CHEESE! ## grabWindow: x: " << inputX << ", y: " << inputY << ", w: " << inputW << ", h: " << inputH << ", name: " << m_topicName << ", id: " << m_topicId << ", desc: " << m_desc;
|
||||
const QPixmap pix = _screen->grabWindow(winId, inputX, inputY, inputW, inputH); // NOTE: LOCAL position for grabWindow!
|
||||
|
||||
if (!pix.save(&m_tmpFile, "PNG"))
|
||||
{
|
||||
onExportFailed(QStringLiteral("Can't save screenshot to file: %1").arg(m_tmpFile.errorString()));
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* class for recording a single topic on a screen
|
||||
* $Id:
|
||||
* $HeadURL:
|
||||
* $Author:
|
||||
* Created by Hannes Fritsche, SLTE, 2023-11-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTemporaryFile>
|
||||
#include <QProcess>
|
||||
#include <Services/PresentationManagement/logger.h>
|
||||
|
||||
class Screen;
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class TopicItem;
|
||||
|
||||
class TopicShotter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public: // methods
|
||||
TopicShotter(const int topicId, const QString &topicName, const Screen *screen, qint32 offset,
|
||||
qint64 startTime, qint64 stopTime, const double topicX, const double topicY,
|
||||
const double topicW, const double topicH, const bool isFilterTopic,
|
||||
const QString &desc, QObject *parent = nullptr);
|
||||
~TopicShotter();
|
||||
|
||||
void saveRecording(const QString &saveToPath);
|
||||
bool isFailed() const { return m_failed; }
|
||||
const QString &topicName() const { return m_topicName; }
|
||||
int topicId() const { return m_topicId; }
|
||||
|
||||
public Q_SLOTS:
|
||||
void onNewTime(qint64 time);
|
||||
|
||||
signals:
|
||||
void exportFailed(const QString &errorMessage);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onExportFailed(const QString &errorMessage);
|
||||
|
||||
private: // attributes
|
||||
const qint64 m_startTime;
|
||||
const QString m_topicName;
|
||||
QTemporaryFile m_tmpFile;
|
||||
const int m_topicId;
|
||||
bool m_failed { false };
|
||||
const Screen *m_screen;
|
||||
const double m_topicX;
|
||||
const double m_topicY;
|
||||
const double m_topicW;
|
||||
const double m_topicH;
|
||||
bool isStarted { false };
|
||||
const bool m_isFilterTopic;
|
||||
QString m_desc;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
}; // class TopicShotter
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#include "pre_topic_track.h"
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
TopicTrack::TopicTrack(const QString &text, const QString &internalName, TrackType trackType)
|
||||
: LtsTimeline::Track(text)
|
||||
, m_trackType(trackType)
|
||||
, m_internalName(internalName)
|
||||
{}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC
|
||||
|
||||
qint64 TopicTrack::firstTopicStart() const
|
||||
{
|
||||
return items().empty() ? -1 : items().front()->startTime();
|
||||
}
|
||||
|
||||
qint64 TopicTrack::lastTopicEnd() const
|
||||
{
|
||||
return items().empty() ? -1 : items().back()->startTime() + items().back()->duration();
|
||||
}
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "pre_utils.h"
|
||||
|
||||
#include <LtsTimeline/track.h>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
class TopicTrack : public LtsTimeline::Track
|
||||
{
|
||||
|
||||
public:
|
||||
TopicTrack(const QString &text, const QString &internalName, TrackType trackType);
|
||||
|
||||
TrackType trackType() const { return m_trackType; }
|
||||
|
||||
QString internalName() const { return m_internalName; }
|
||||
void setInternalName(const QString &name) { m_internalName = name; }
|
||||
|
||||
qint64 firstTopicStart() const;
|
||||
qint64 lastTopicEnd() const;
|
||||
|
||||
private:
|
||||
TrackType m_trackType;
|
||||
QString m_internalName;
|
||||
|
||||
};
|
||||
|
||||
} // namespace PresentationManagement
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
#include "pre_utils.h"
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
QString trackTypeString(TrackType trackType)
|
||||
{
|
||||
switch (trackType)
|
||||
{
|
||||
case TrackType::Audio:
|
||||
return QStringLiteral("Audio");
|
||||
case TrackType::Replay:
|
||||
return QStringLiteral("Playback");
|
||||
case TrackType::Screen:
|
||||
return QStringLiteral("Screen");
|
||||
case TrackType::Filter:
|
||||
return QStringLiteral("Filter");
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, TrackType trackType)
|
||||
{
|
||||
stream << "TrackType::" << trackTypeString(trackType).toStdString().c_str();
|
||||
return stream;
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <QVector>
|
||||
|
||||
|
||||
namespace PresentationManagement
|
||||
{
|
||||
|
||||
enum class TrackType
|
||||
{
|
||||
Audio,
|
||||
Replay,
|
||||
Screen,
|
||||
Filter
|
||||
};
|
||||
typedef QVector<TrackType> TrackTypes;
|
||||
|
||||
QString trackTypeString(TrackType trackType);
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, TrackType trackType);
|
||||
|
||||
} // PresentationManagement
|
||||
Reference in new issue
Block a user