Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+260
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - AVS Sequence Model
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2017-08-21
|
||||
*/
|
||||
|
||||
#include "avm_sequence_model.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QColor>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
AvmSequenceModel::AvmSequenceModel(QObject *parent)
|
||||
: LtsWidgets::LtsAbstractTableModel<AvsSequence>({QLatin1String("Order"),
|
||||
tr("Start"),
|
||||
tr("End"),
|
||||
tr("Duration [s]"),
|
||||
tr("Stream"),
|
||||
tr("Speed"),
|
||||
tr("Volume"),
|
||||
tr("Description")},
|
||||
parent)
|
||||
, m_logger(getLogger("AvmSequenceModel"))
|
||||
{}
|
||||
|
||||
void AvmSequenceModel::setTimezoneHoursOffset(int timezoneHoursOffset)
|
||||
{
|
||||
m_timezoneHoursOffset = timezoneHoursOffset;
|
||||
emit dataChanged(index(0, Start), index(rowCount()-1, End), {Qt::DisplayRole});
|
||||
}
|
||||
|
||||
void AvmSequenceModel::moveUp(const QModelIndex &idx)
|
||||
{
|
||||
// should better be done within moveRows
|
||||
int row = idx.row();
|
||||
auto it = std::find_if(m_highlightedRows.begin(), m_highlightedRows.end(), [row](int currentRow)
|
||||
{
|
||||
return (row == currentRow);
|
||||
});
|
||||
|
||||
if (it != m_highlightedRows.end())
|
||||
(*it)--;
|
||||
|
||||
moveRows(idx.parent(), idx.row(), 1, idx.parent(), idx.row() - 1);
|
||||
}
|
||||
|
||||
void AvmSequenceModel::moveDown(const QModelIndex &idx)
|
||||
{
|
||||
// should better be done within moveRows
|
||||
int row = idx.row();
|
||||
auto it = std::find_if(m_highlightedRows.begin(), m_highlightedRows.end(), [row](int currentRow)
|
||||
{
|
||||
return (row == currentRow);
|
||||
});
|
||||
|
||||
if (it != m_highlightedRows.end())
|
||||
(*it)++;
|
||||
moveRows(idx.parent(), idx.row(), 1, idx.parent(), idx.row() + 1);
|
||||
}
|
||||
|
||||
bool AvmSequenceModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
|
||||
{
|
||||
Q_UNUSED(count)
|
||||
if(sourceRow > m_vecData.size() -1 || sourceRow < 0 ||
|
||||
destinationChild > m_vecData.size() -1 || destinationChild < 0)
|
||||
return false;
|
||||
|
||||
int moveTo = destinationChild;
|
||||
if (destinationChild > sourceRow)
|
||||
moveTo++;
|
||||
|
||||
beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent, moveTo);
|
||||
m_vecData.move(sourceRow, destinationChild);
|
||||
endMoveRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline std::chrono::milliseconds fromJsonValue(const QJsonValue &val)
|
||||
{
|
||||
qint64 d = static_cast<qint64>(val.toDouble());
|
||||
return std::chrono::milliseconds(d);
|
||||
}
|
||||
|
||||
bool AvmSequenceModel::initModel(const QJsonArray &data)
|
||||
{
|
||||
m_highlightedRows.clear();
|
||||
clear();
|
||||
|
||||
AvsSequence seq;
|
||||
for (const auto &ele : data)
|
||||
{
|
||||
const QJsonObject &seqObj = ele.toObject();
|
||||
if (seqObj.isEmpty())
|
||||
continue;
|
||||
seq.internalStreamName = seqObj.value(QStringLiteral("stream")).toString();
|
||||
seq.speed = seqObj.value(QStringLiteral("speed")).toInt();
|
||||
seq.volume = seqObj.value(QStringLiteral("volume")).toInt();
|
||||
seq.start = AvaTimePoint(std::chrono::duration_cast<AvaDuration>(fromJsonValue(seqObj.value(QStringLiteral("start")))));
|
||||
seq.duration = std::chrono::duration_cast<AvaDuration>(fromJsonValue(seqObj.value(QStringLiteral("duration"))));
|
||||
seq.pint = seqObj.value(QStringLiteral("pint")).toBool();
|
||||
seq.description = seqObj.value(QStringLiteral("description")).toString();
|
||||
insertRow(seq);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonArray AvmSequenceModel::getSequencesAsJsonArray() const
|
||||
{
|
||||
QJsonArray ret;
|
||||
|
||||
if (m_vecData.size() <= 0)
|
||||
return ret;
|
||||
|
||||
for (int i = 0; i < m_vecData.size(); ++i)
|
||||
{
|
||||
const AvsSequence &seq = m_vecData.at(i);
|
||||
QJsonObject seqJson;
|
||||
seqJson.insert(QLatin1String("pos"), i);
|
||||
seqJson.insert(QLatin1String("description"), seq.description);
|
||||
|
||||
seqJson.insert(QLatin1String("start"), static_cast<qint64>(std::chrono::milliseconds(seq.start.time_since_epoch()).count()));
|
||||
seqJson.insert(QLatin1String("duration"), static_cast<qint64>(std::chrono::milliseconds(seq.duration).count()));
|
||||
seqJson.insert(QLatin1String("speed"), static_cast<int>(seq.speed));
|
||||
seqJson.insert(QLatin1String("stream"), seq.internalStreamName);
|
||||
seqJson.insert(QLatin1String("volume"), static_cast<int>(seq.volume));
|
||||
seqJson.insert(QLatin1String("pint"), seq.pint);
|
||||
ret.append(seqJson);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QString AvmSequenceModel::dateTimeToStringx(const QDateTime &dt) const
|
||||
{
|
||||
return QDateTime::fromMSecsSinceEpoch(dt.toMSecsSinceEpoch() + (m_timezoneHoursOffset * 3600 * 1000)).toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz"));
|
||||
}
|
||||
|
||||
|
||||
QString AvmSequenceModel::msecsSinceEpochToStringx(qint64 msecs) const
|
||||
{
|
||||
return dateTimeToStringx(QDateTime::fromMSecsSinceEpoch(msecs));
|
||||
}
|
||||
|
||||
|
||||
QVariant AvmSequenceModel::data(const QModelIndex &idx, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole && role != Qt::BackgroundRole)
|
||||
return QVariant();
|
||||
|
||||
if (!indexIsValid(idx))
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
const AvsSequence &seq = m_vecData.at(idx.row());
|
||||
switch (idx.column())
|
||||
{
|
||||
case Order:
|
||||
return idx.row() + 1;
|
||||
case Start:
|
||||
return msecsSinceEpochToStringx(std::chrono::milliseconds(seq.start.time_since_epoch()).count());
|
||||
case End:
|
||||
return msecsSinceEpochToStringx(std::chrono::milliseconds((seq.start+seq.duration).time_since_epoch()).count());
|
||||
case Duration:
|
||||
{
|
||||
qint64 duration = std::chrono::milliseconds(seq.duration).count();
|
||||
qint64 durationDisplay = duration / seq.speed * 100;
|
||||
|
||||
qint64 secs = durationDisplay / 1000;
|
||||
qint64 msecs = (durationDisplay % 1000);
|
||||
|
||||
return QStringLiteral("%1.%2").arg(secs).arg(msecs, 3, 10, QLatin1Char('0'));
|
||||
}
|
||||
case Description:
|
||||
return seq.description;
|
||||
case Streams:
|
||||
return seq.internalStreamName;
|
||||
case Volume:
|
||||
return QVariant(seq.volume);
|
||||
case Speed:
|
||||
return QVariant(seq.speed);
|
||||
}
|
||||
}
|
||||
else if (role == Qt::BackgroundRole)
|
||||
{
|
||||
if (m_highlightedRows.contains(idx.row()))
|
||||
return QColor(Qt::green);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void AvmSequenceModel::setHighlightedRow(int highlighted, bool reset)
|
||||
{
|
||||
QVector<int> oldSelected = m_highlightedRows;
|
||||
|
||||
if (highlighted < 0)
|
||||
m_highlightedRows.clear();
|
||||
else if (reset)
|
||||
m_highlightedRows = { highlighted };
|
||||
else
|
||||
m_highlightedRows += highlighted;
|
||||
|
||||
if (m_highlightedRows == oldSelected)
|
||||
return; // no row data changed, nothing to do
|
||||
|
||||
QVector<int> changedRows;
|
||||
changedRows.reserve(oldSelected.size() + m_highlightedRows.size());
|
||||
|
||||
if (m_highlightedRows.isEmpty() || reset)
|
||||
for (int row : qAsConst(oldSelected))
|
||||
changedRows += row;
|
||||
|
||||
for (int row : qAsConst(m_highlightedRows))
|
||||
changedRows += row;
|
||||
|
||||
for (int row : qAsConst(changedRows))
|
||||
emit dataChanged(index(row, 0), index(row, Description), { Qt::BackgroundRole });
|
||||
}
|
||||
|
||||
void AvmSequenceProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
|
||||
{
|
||||
m_sourceModel = qobject_cast<AvmSequenceModel*>(sourceModel);
|
||||
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
|
||||
}
|
||||
|
||||
bool AvmSequenceProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||
{
|
||||
if (!m_sourceModel ||
|
||||
source_left.column() != source_right.column() ||
|
||||
!source_left.isValid() ||
|
||||
!source_right.isValid())
|
||||
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
|
||||
const auto &dataLeft = m_sourceModel->m_vecData.at(source_left.row());
|
||||
const auto dataRight = m_sourceModel->m_vecData.at(source_right.row());
|
||||
switch (source_left.column())
|
||||
{
|
||||
case AvmSequenceModel::Start:
|
||||
return std::chrono::milliseconds(dataLeft.start.time_since_epoch()).count() < std::chrono::milliseconds(dataRight.start.time_since_epoch()).count();
|
||||
case AvmSequenceModel::End:
|
||||
return std::chrono::milliseconds((dataLeft.start+dataLeft.duration).time_since_epoch()).count() < std::chrono::milliseconds((dataRight.start+dataLeft.duration).time_since_epoch()).count();
|
||||
case AvmSequenceModel::Order:
|
||||
case AvmSequenceModel::Speed:
|
||||
case AvmSequenceModel::Streams:
|
||||
case AvmSequenceModel::Volume:
|
||||
case AvmSequenceModel::Description:
|
||||
break;
|
||||
}
|
||||
|
||||
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - AVS Sequence Model
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2017-08-21
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LtsWidgets/lts_abstract_table_model.h>
|
||||
#include <LtsWidgets/lts_column_filter_proxy_model.h>
|
||||
#include <AudioVideoManagement/logger.h>
|
||||
#include <AudioVideoManagement/ava_types.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
class AvsSequence
|
||||
{
|
||||
public:
|
||||
QString internalStreamName;
|
||||
AvaSpeed speed{100};
|
||||
AvaVolume volume{0};
|
||||
AvaTimePoint start;
|
||||
AvaDuration duration;
|
||||
bool pint = false;
|
||||
QString description;
|
||||
};
|
||||
|
||||
|
||||
class AvmSequenceModel final : public LtsWidgets::LtsAbstractTableModel<AvsSequence>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Columns
|
||||
{
|
||||
Order = 0,
|
||||
Start,
|
||||
End,
|
||||
Duration,
|
||||
Streams,
|
||||
Speed,
|
||||
Volume,
|
||||
Description
|
||||
};
|
||||
|
||||
explicit AvmSequenceModel(QObject *parent = nullptr);
|
||||
void moveUp(const QModelIndex &idx);
|
||||
void moveDown(const QModelIndex &idx);
|
||||
bool initModel(const QJsonArray &data);
|
||||
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
|
||||
QJsonArray getSequencesAsJsonArray() const;
|
||||
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||
QVector<int> highlightedRows() const { return m_highlightedRows; }
|
||||
void setHighlightedRow(int highlighted, bool reset = true);
|
||||
|
||||
friend class AvmSequenceProxyModel;
|
||||
|
||||
public slots:
|
||||
void setTimezoneHoursOffset(int timezoneHoursOffset);
|
||||
|
||||
private:
|
||||
QString dateTimeToStringx(const QDateTime &dt) const;
|
||||
QString msecsSinceEpochToStringx(qint64 msecs) const;
|
||||
|
||||
QVector<int> m_highlightedRows;
|
||||
int m_timezoneHoursOffset = 0;
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
class AvmSequenceProxyModel final : public LtsWidgets::LtsColumnFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using LtsWidgets::LtsColumnFilterProxyModel::LtsColumnFilterProxyModel;
|
||||
void setSourceModel(QAbstractItemModel *sourceModel) override;
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
|
||||
private:
|
||||
AvmSequenceModel *m_sourceModel = nullptr;
|
||||
};
|
||||
}
|
||||
+684
@@ -0,0 +1,684 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Topic Editor for the Situation Topic in the Script Editor
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-04-04
|
||||
*/
|
||||
|
||||
#include "topic_editor.h"
|
||||
#include "ui_topic_editor.h"
|
||||
|
||||
#include <AudioVideoManagement/av_helper.h>
|
||||
#include <AudioVideoManagement/ava_comm.h>
|
||||
#include <AudioVideoManagement/ava_session.h>
|
||||
#include <AudioVideoManagement/avs_dialog.h>
|
||||
#include <AudioVideoManagement/Shared/playback_app.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
#include <TopicEditorBase/topic_previewer.h>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <cmath>
|
||||
|
||||
#include <QJsonArray>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
|
||||
#include <QScopeGuard>
|
||||
#endif
|
||||
|
||||
|
||||
static constexpr int s_defaultOffset = 10 * 1000; // 10s
|
||||
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
QString dateTimeToStringz(const QDateTime &dt)
|
||||
{
|
||||
return dt.toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz"));
|
||||
}
|
||||
|
||||
QString msecsSinceEpochToStringz(qint64 msecs)
|
||||
{
|
||||
return dateTimeToStringz(QDateTime::fromMSecsSinceEpoch(msecs));
|
||||
}
|
||||
|
||||
qint64 timePointToMsecs(const AvaTimePoint &atp)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(atp.time_since_epoch()).count();
|
||||
}
|
||||
|
||||
AvaDuration msecsToAvaDuration(qint64 msecs)
|
||||
{
|
||||
return std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(msecs));
|
||||
}
|
||||
|
||||
AvaTimePoint msecsToAvaTimePoint(qint64 msecs)
|
||||
{
|
||||
return AvaTimePoint(msecsToAvaDuration(msecs));
|
||||
}
|
||||
|
||||
|
||||
TopicEditor::TopicEditor(LtsSession::LtsSession *session, const TopicEditorBase::TopicData &data, QWidget *parent)
|
||||
: TopicEditorBase::TopicEditor(parent)
|
||||
, m_session(session)
|
||||
, m_avmDataIfc(nullptr)
|
||||
, m_model(new AvmSequenceModel(this))
|
||||
, m_playbackMenu(nullptr)
|
||||
, m_ui(new Ui::AVTopicEditor)
|
||||
, m_logger(getLogger("TopicEditor"))
|
||||
{
|
||||
Q_ASSERT(session);
|
||||
|
||||
m_ui->setupUi(this);
|
||||
m_ui->tvSequences->hideColumn(AvmSequenceModel::Order);
|
||||
init(m_ui->widget);
|
||||
|
||||
m_isAudio = (data.topicType == TopicEditorBase::TopicType::Audio);
|
||||
if (m_isAudio)
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Add_Audio_Topic"));
|
||||
else
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Add_Video_Topic"));
|
||||
|
||||
setTopicData(data);
|
||||
m_topicData.properties[QStringLiteral("IsAudio")] = bool(m_topicData.topicType == TopicEditorBase::TopicType::Audio);
|
||||
m_ui->widget->previewer()->setTopicData(m_topicData);
|
||||
|
||||
QJsonArray arr = getTopicVDataAsArray(QLatin1String("Sequences"));
|
||||
m_model->initModel(arr);
|
||||
m_ui->tvSequences->resizeColumnsToContents();
|
||||
refreshDurationText();
|
||||
m_proxyModel = new AvmSequenceProxyModel(this);
|
||||
m_proxyModel->setSourceModel(m_model);
|
||||
m_ui->tvSequences->setModel(m_proxyModel);
|
||||
connect(m_ui->tvSequences, SIGNAL(doubleClicked(QModelIndex)),
|
||||
m_ui->tvSequences->selectionModel(), SLOT(select(QModelIndex, QItemSelectionModel::Toggle)));
|
||||
|
||||
connect(m_ui->tvSequences->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, &TopicEditor::onEnaDisa);
|
||||
connect(m_ui->pbDelete, &QToolButton::clicked,
|
||||
this, &TopicEditor::onDeleteSequences);
|
||||
connect(m_ui->pbUp, &QToolButton::clicked,
|
||||
this, &TopicEditor::onSequenceUp);
|
||||
connect(m_ui->pbDown, &QToolButton::clicked,
|
||||
this, &TopicEditor::onSequenceDown);
|
||||
connect(m_ui->pbPreview, &QPushButton::clicked, this, [this]()
|
||||
{
|
||||
m_playbackMenu->setStreamTime(m_ui->dtStart->msecsSinceEpoch());
|
||||
});
|
||||
|
||||
if (!m_session->getForeignDataInterface_NEW<AudioVideoManagement::DataInterface>(m_avmDataIfc))
|
||||
{
|
||||
playbackDlgNotAvailable(tr("The audio/video interface is not available in this session."));
|
||||
return;
|
||||
}
|
||||
|
||||
PlaybackApp *app = m_isAudio ? m_avmDataIfc->audioPlaybackApp() : m_avmDataIfc->videoPlaybackApp();
|
||||
if (app == nullptr)
|
||||
{
|
||||
playbackDlgNotAvailable(tr("The audio/video interface is currently not available."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (app->inUse())
|
||||
{
|
||||
playbackDlgNotAvailable(tr("The audio/video interface is already in use by another dialog."));
|
||||
return;
|
||||
}
|
||||
|
||||
// AFTER THIS POINT IT IS GUARANTEED TO HAVE A PLAYBACK DIALOG!
|
||||
|
||||
m_ui->pbApply->setEnabled(false);
|
||||
m_ui->pbDelete->setEnabled(false);
|
||||
m_ui->pbUp->setEnabled(false);
|
||||
m_ui->pbDown->setEnabled(false);
|
||||
|
||||
// playback menu
|
||||
boost::optional<QString> stream = boost::none; // will be used as stream preset
|
||||
if (m_topicData.properties.contains(QStringLiteral("stream")))
|
||||
stream = m_topicData.properties.value(QStringLiteral("stream")).toString();
|
||||
|
||||
// get preset start time from topic (e.g. from evaluation topic)
|
||||
qint64 startMsecs = 0;
|
||||
boost::optional<AvaTimePoint> start = boost::none; // will be used as current time preset
|
||||
{
|
||||
const QJsonValue jsonValue(getTopicDataValue(QStringLiteral("start")));
|
||||
if (jsonValue.isDouble())
|
||||
{
|
||||
startMsecs = static_cast<qint64>(jsonValue.toDouble());
|
||||
start = boost::optional<AvaTimePoint>(toTimePoint(startMsecs));
|
||||
}
|
||||
}
|
||||
|
||||
// get preset end time from topic (e.g. from evaluation topic)
|
||||
qint64 endMsecs = 0;
|
||||
{
|
||||
const QJsonValue jsonValue(getTopicDataValue(QStringLiteral("end")));
|
||||
if (jsonValue.isDouble())
|
||||
endMsecs = static_cast<qint64>(jsonValue.toDouble());
|
||||
}
|
||||
|
||||
m_playbackMenu = new AvmPlaybackMenu(this);
|
||||
m_playbackMenu->init(
|
||||
m_session, m_avmDataIfc->avaSession(), PlaybackAppHandle(app), m_avmDataIfc->streamNameMapping(), m_isAudio, true /* is inside topic editor */,
|
||||
stream, start
|
||||
);
|
||||
m_ui->playbackParent->layout()->addWidget(m_playbackMenu);
|
||||
//m_playbackDlg->hideStreamSelection();
|
||||
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::setTopicButtonStates,
|
||||
this, &TopicEditor::onSetTopicButtonStates);
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::markerSelected,
|
||||
this, &TopicEditor::onMarkerSelected);
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalCurrentStreamChanged, this, [this](const QString &displayName)
|
||||
{
|
||||
m_ui->lblStream->setText(displayName);
|
||||
});
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalSetStart, this, [this](qint64 start)
|
||||
{
|
||||
m_ui->dtStart->setMsecsSinceEpoch(start);
|
||||
if (m_ui->dtEnd->msecsSinceEpoch() < start)
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(start + s_defaultOffset);
|
||||
});
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalSetEnd, this, [this](qint64 end)
|
||||
{
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(end);
|
||||
if (m_ui->dtStart->msecsSinceEpoch() > end)
|
||||
m_ui->dtStart->setMsecsSinceEpoch(end - s_defaultOffset);
|
||||
});
|
||||
m_playbackMenu->show();
|
||||
|
||||
// NOTE: don't confuse the time *inside* the script timeline with a time since epoch!! following code lines do not make sense:
|
||||
|
||||
// // initial values
|
||||
if (m_topicData.startTime > -1)
|
||||
{
|
||||
m_ui->dtStart->setMsecsSinceEpoch(static_cast<qint64>(m_topicData.startTime) * 1000);
|
||||
if (m_topicData.duration > 0)
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(static_cast<qint64>(m_topicData.startTime + m_topicData.duration) * 1000);
|
||||
} // if
|
||||
|
||||
// the playback dialog part of the topic editor
|
||||
connect(m_ui->tvSequences, &QTableView::doubleClicked,
|
||||
this, &TopicEditor::onEditSequence);
|
||||
connect(m_ui->dtStart, &LtsWidgets::LtsMSecDateTimeEdit::dateTimeChanged,
|
||||
this, &TopicEditor::onTimeChanged);
|
||||
connect(m_ui->dtEnd, &LtsWidgets::LtsMSecDateTimeEdit::dateTimeChanged,
|
||||
this, &TopicEditor::onTimeChanged);
|
||||
connect(m_ui->pbAdd, &QToolButton::clicked,
|
||||
this, &TopicEditor::onAddSequence);
|
||||
connect(m_ui->pbApply, &QToolButton::clicked,
|
||||
this, &TopicEditor::onApplySequence);
|
||||
connect(m_ui->pbEditSequence, &QPushButton::clicked,
|
||||
this, &TopicEditor::onEditSequence);
|
||||
|
||||
// if start and end times were set (e.g. by evaluation topic), then auto set them as start & end time for the new sequence
|
||||
if (startMsecs > 0 && endMsecs > 0)
|
||||
{
|
||||
m_ui->dtStart->setMsecsSinceEpoch(startMsecs);
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(endMsecs);
|
||||
}
|
||||
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalTimezoneChanged,
|
||||
m_ui->dtStart, &LtsWidgets::LtsMSecDateTimeEdit::setTimezoneHoursOffset);
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalTimezoneChanged,
|
||||
m_ui->dtEnd, &LtsWidgets::LtsMSecDateTimeEdit::setTimezoneHoursOffset);
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalTimezoneChanged,
|
||||
m_model, &AvmSequenceModel::setTimezoneHoursOffset);
|
||||
|
||||
int offset = m_playbackMenu->currentOffset();
|
||||
m_model->setTimezoneHoursOffset(offset); // as the emit happens before the commits!
|
||||
m_ui->dtStart->setTimezoneHoursOffset(offset);
|
||||
m_ui->dtEnd->setTimezoneHoursOffset(offset);
|
||||
}
|
||||
|
||||
TopicEditor::~TopicEditor()
|
||||
{
|
||||
delete m_ui;
|
||||
delete m_model;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE
|
||||
|
||||
void TopicEditor::updateInternalData()
|
||||
{
|
||||
if (hasTopicDataProperties())
|
||||
{
|
||||
m_topicStartTime = getTopicDataValue(QStringLiteral("start")).toDouble();
|
||||
m_topicStreamName = getTopicDataValue(QStringLiteral("stream")).toString();
|
||||
QJsonArray arr = getTopicVDataAsArray(QLatin1String("Sequences"));
|
||||
m_model->initModel(arr);
|
||||
m_ui->tvSequences->resizeColumnsToContents();
|
||||
setDurationText(calcTrackLength(arr));
|
||||
}
|
||||
|
||||
m_ui->pbApply->setEnabled(false);
|
||||
m_ui->pbDelete->setEnabled(false);
|
||||
}
|
||||
|
||||
void TopicEditor::retranslate()
|
||||
{
|
||||
// translate(m_ui, trackEntryType() == TopicType::Audio ? tr("Audio Topic Editor") : tr("Video Topic Editor") );
|
||||
translate(m_ui, m_topicData.topicType == TopicEditorBase::TopicType::Audio ? tr("Audio Topic Editor") : tr("Video Topic Editor") );
|
||||
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->setText(tr("Apply to topic", "Auf Topic übernehmen"));
|
||||
}
|
||||
|
||||
|
||||
AvsSequence TopicEditor::createSequence(const AvaTimePoint &start, const AvaTimePoint &end) const
|
||||
{
|
||||
AvsSequence seq;
|
||||
|
||||
seq.start = start;
|
||||
seq.duration = end - start; // not sure if AVS needs duration or end
|
||||
seq.description = m_ui->leDescription->text();
|
||||
|
||||
if (m_playbackMenu != nullptr)
|
||||
{
|
||||
seq.speed = m_playbackMenu->getSpeed();
|
||||
seq.volume = m_playbackMenu->getVolume();
|
||||
seq.internalStreamName = m_playbackMenu->getInternalStreamName();
|
||||
seq.pint = m_playbackMenu->getSkipPause();
|
||||
}
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
AvsSequence TopicEditor::createCurrentSequence() const
|
||||
{
|
||||
return createSequence(AvaTimePoint(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(m_ui->dtStart->msecsSinceEpoch()))),
|
||||
AvaTimePoint(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(m_ui->dtEnd->msecsSinceEpoch()))));
|
||||
}
|
||||
|
||||
void TopicEditor::refreshDurationText()
|
||||
{
|
||||
setDurationText(calcTrackLength(m_model->getSequencesAsJsonArray()));
|
||||
}
|
||||
|
||||
void TopicEditor::setDurationText(qint64 length)
|
||||
{
|
||||
if (length > s_durationLimit)
|
||||
m_ui->lblDuration->setText(tr("Playback too long!"));
|
||||
else
|
||||
m_ui->lblDuration->setText(QTime::fromMSecsSinceStartOfDay(length).toString(QStringLiteral("HH:mm:ss.zzz")));
|
||||
}
|
||||
|
||||
qint64 TopicEditor::calcTrackLength(const QJsonArray &arr) const
|
||||
{
|
||||
qint64 length = 0;
|
||||
for (const auto &ele : arr)
|
||||
{
|
||||
const QJsonObject &seqObj = ele.toObject();
|
||||
if (seqObj.isEmpty())
|
||||
continue;
|
||||
// length += std::ceil(seqObj.value(QLatin1String("duration")).toDouble() * (std::fabs(seqObj.value(QLatin1String("speed")).toDouble(100)) / 100)); Original
|
||||
length += std::ceil(seqObj.value(QLatin1String("duration")).toDouble() / (std::fabs(seqObj.value(QLatin1String("speed")).toDouble(100))) * 100 );
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void TopicEditor::playbackDlgNotAvailable(const QString &msg)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, msg);
|
||||
m_ui->groupBox->hide();
|
||||
m_ui->line->hide();
|
||||
|
||||
// left side:
|
||||
m_ui->pbEditSequence->hide();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE SLOTS
|
||||
|
||||
bool TopicEditor::apply2()
|
||||
{
|
||||
const QJsonArray &arr = m_model->getSequencesAsJsonArray();
|
||||
qint64 duration = calcTrackLength(arr);
|
||||
|
||||
if (duration > s_durationLimit)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("This topic has exceeded the maximum length of %1 seconds.").arg(s_durationLimit));
|
||||
return false;
|
||||
}
|
||||
|
||||
setDurationText(duration);
|
||||
|
||||
if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("AVM.AllowIncompleteTopic")) != QVariant(true)) // don't execute code block if AllowIncompleteTopic is set !
|
||||
{
|
||||
if (duration <= 0)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(
|
||||
this, tr("The total playback length is less than 1 second long. <br>"
|
||||
"Please verify that you have one or more sequences in the sequences list and that these sequences are of at least 1 second length."),
|
||||
tr("Playback length too short")
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 secs = duration / 1000; // strip msecs ...
|
||||
qint64 msecs = duration - (secs * 1000); // ... and set to zero !
|
||||
qWarning() << "secs" << secs << "msecs" << msecs;
|
||||
|
||||
// a ratio between 500 missing mseconds of a video and 500 msecs of no video output seems balanced
|
||||
if (msecs >= 500)
|
||||
++secs;
|
||||
qWarning() << "secs now" << secs;
|
||||
m_topicData.duration = secs;
|
||||
}
|
||||
else // AllowIncompleteTopic is set! fake it!
|
||||
m_topicData.duration = 10;
|
||||
|
||||
setTopicDataProperty(QStringLiteral("Sequences"), m_model->getSequencesAsJsonArray());
|
||||
setTopicDataProperty(QStringLiteral("ScriptName"), QString(QStringLiteral("S") + QString::number(m_topicData.id)));
|
||||
setTopicDataProperty(QStringLiteral("IsAudio"), bool(m_topicData.topicType == TopicEditorBase::TopicType::Audio));
|
||||
setDescription(m_ui->widget->description());
|
||||
m_ui->widget->previewer()->setTopicData(m_topicData);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TopicEditor::onEditSequence()
|
||||
{
|
||||
if (!m_playbackMenu)
|
||||
return;
|
||||
|
||||
DEBUG_LOG(m_logger) << "slotEditSequence ";
|
||||
const auto selRows = m_ui->tvSequences->selectionModel()->selectedRows();
|
||||
|
||||
if (selRows.size() != 1)
|
||||
{
|
||||
m_model->setHighlightedRow(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
int row = selRows.at(0).row();
|
||||
m_model->setHighlightedRow(row);
|
||||
const AvsSequence selectedSeq = m_model->getRowData(row);
|
||||
|
||||
DEBUG_LOG(m_logger) << "selectedSeq.internalStreamName=" << selectedSeq.internalStreamName;
|
||||
m_playbackMenu->setStreamName(selectedSeq.internalStreamName);
|
||||
|
||||
m_ui->dtStart->setMsecsSinceEpoch(std::chrono::milliseconds(selectedSeq.start.time_since_epoch()).count());
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(std::chrono::milliseconds((selectedSeq.start + selectedSeq.duration).time_since_epoch()).count());
|
||||
|
||||
m_ui->leDescription->setText(selectedSeq.description);
|
||||
m_playbackMenu->setStreamByName(selectedSeq.internalStreamName);
|
||||
m_playbackMenu->setSpeed(selectedSeq.speed);
|
||||
m_playbackMenu->setVolume(selectedSeq.volume);
|
||||
m_playbackMenu->setSkipPause(selectedSeq.pint);
|
||||
m_playbackMenu->setSavedPos(m_ui->dtStart->msecsSinceEpoch()); // plays that timepoint as soon as the stream was selected and QueryTimeline was successful not working.. : m_playbackDlg->setStreamTime(m_ui->dteStart->msecsSinceEpoch());
|
||||
|
||||
m_ui->pbApply->setEnabled(true);
|
||||
}
|
||||
|
||||
void TopicEditor::onTimeChanged()
|
||||
{
|
||||
/* user gets a warning on 'add' if times don't add up, otherwise there is too much movement in the end-time dte! */
|
||||
|
||||
|
||||
// LtsWidgets::LtsMSecDateTimeEdit *dtSender = qobject_cast<LtsWidgets::LtsMSecDateTimeEdit*>(sender());
|
||||
|
||||
// if (!dtSender)
|
||||
// return;
|
||||
|
||||
// if (dtSender == m_ui->dtEnd)
|
||||
// {
|
||||
// if (m_ui->dtEnd->msecsSinceEpoch() < m_ui->dtStart->msecsSinceEpoch())
|
||||
// {
|
||||
// qint64 duration = static_cast<qint64>(m_trackEntry->m_length > 0 ? m_trackEntry->m_length : s_defaultOffset) * 1000;
|
||||
// m_ui->dteStart->setMsecsSinceEpoch(m_ui->dteEnd->msecsSinceEpoch() - duration);
|
||||
// } // if
|
||||
// } // if
|
||||
// else // start is sender
|
||||
// {
|
||||
// if (m_ui->dteEnd->msecsSinceEpoch() < m_ui->dteStart->msecsSinceEpoch())
|
||||
// {
|
||||
// qint64 duration = static_cast<qint64>(m_trackEntry->m_length > 0 ? m_trackEntry->m_length : s_defaultOffset) * 1000;
|
||||
// m_ui->dteEnd->setMsecsSinceEpoch(m_ui->dteStart->msecsSinceEpoch() + duration);
|
||||
// } // if
|
||||
// } // else
|
||||
}
|
||||
|
||||
void TopicEditor::onApplySequence()
|
||||
{
|
||||
const auto seq = createCurrentSequence();
|
||||
if (seq.start.time_since_epoch().count() == 0 || seq.duration.count() == 0)
|
||||
return;
|
||||
|
||||
const auto rows = m_model->highlightedRows();
|
||||
|
||||
if (rows.count() != 1)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Please select a sequence from the sequence list first, then click 'Edit' to edit a sequence in the editor."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->setRowData(rows.first(), seq);
|
||||
|
||||
refreshDurationText();
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onAddSequence()
|
||||
{
|
||||
qint64 start = m_ui->dtStart->msecsSinceEpoch();
|
||||
qint64 end = m_ui->dtEnd->msecsSinceEpoch();
|
||||
|
||||
if (start == 0 || end == 0 || end < start)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Please check if start and end times are set and the end time is not lower than the start time."),
|
||||
tr("The start and/or end time you entered is invalid"));
|
||||
return;
|
||||
}
|
||||
|
||||
AvsSequence seq = createCurrentSequence();
|
||||
|
||||
if (seq.pint) // if empty parts should be skipped, the duration has to be calculated (as there can be more than 1 skipped / empty part in a sequence)
|
||||
{
|
||||
// async!
|
||||
|
||||
AvaTimePoint theStart = msecsToAvaTimePoint(m_ui->dtStart->msecsSinceEpoch());
|
||||
AvaTimePoint theEnd = msecsToAvaTimePoint(m_ui->dtEnd->msecsSinceEpoch());
|
||||
|
||||
QueryTimeline *qt = m_playbackMenu->queryTimeline(theStart, theEnd);
|
||||
|
||||
if (qt == nullptr)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Currently unable to request sequences from the audio / video system.<br />Please try again later."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
setOverlay(new QWidget());
|
||||
|
||||
|
||||
connect(qt, &QueryTimeline::signalTimelineComplete,
|
||||
this, [this, theStart, theEnd](const AvaTimeline &timeline)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
|
||||
auto unchangableReset = qScopeGuard([this] { unsetOverlay(); });
|
||||
#else
|
||||
std::shared_ptr<int> unchangeableReset(nullptr, [this](int *) { unsetOverlay(); });
|
||||
#endif
|
||||
|
||||
static const QString msgTitle(tr("Sequences needed to be determined for audio / video clips with skipped silence"));
|
||||
const QVector<AvaTimelinePart> &parts = timeline.parts();
|
||||
|
||||
if (parts.isEmpty())
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("No sequences could be found inside the specified range. <br />Please select another range that contains audio / video."),
|
||||
msgTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList partsStrings;
|
||||
|
||||
for (const AvaTimelinePart &part : parts)
|
||||
{
|
||||
AvaTimePoint start = part.first;
|
||||
AvaTimePoint end = part.second;
|
||||
|
||||
if (theStart > start)
|
||||
start = theStart;
|
||||
if (theEnd < end)
|
||||
end = theEnd;
|
||||
|
||||
qint64 duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
qint64 secs = duration / 1000;
|
||||
qint64 msecs = (duration % 1000);
|
||||
QString durationString(QStringLiteral("%1.%2").arg(secs).arg(msecs, 3, 10, QLatin1Char('0')));
|
||||
|
||||
partsStrings += QStringLiteral("<span>%1 - %2 (%3 %4)</span>").arg(msecsSinceEpochToStringz(std::chrono::milliseconds(start.time_since_epoch()).count()),
|
||||
msecsSinceEpochToStringz(std::chrono::milliseconds(end.time_since_epoch()).count()),
|
||||
durationString,
|
||||
tr("seconds"));
|
||||
}
|
||||
|
||||
|
||||
// Patrick said the message is not needed
|
||||
//#define AskUserSequenceList
|
||||
#ifdef AskUserSequenceList
|
||||
auto msg = new LtsWidgets::LtsMessageDialogNew(this, tr("The following sequences were determined: <br /> <br /> %1 <br /> <br />Do you want to add the listed sequences to the sequences list?").arg(partsStrings.join(QStringLiteral("<br />"))),
|
||||
msgTitle, LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::Cancel, { { LtsWidgets::Button::Yes, tr("Add") } });
|
||||
|
||||
connect(msg, &LtsWidgets::LtsMessageDialogNew::yes,
|
||||
this, [this, parts, theStart, theEnd]()
|
||||
#endif
|
||||
{
|
||||
m_model->setHighlightedRow(-1); // initial highlight reset
|
||||
|
||||
for (const AvaTimelinePart &part : parts)
|
||||
{
|
||||
AvaTimePoint start = part.first;
|
||||
AvaTimePoint end = part.second;
|
||||
|
||||
if (theStart > start)
|
||||
start = theStart;
|
||||
if (theEnd < end)
|
||||
end = theEnd;
|
||||
|
||||
m_model->insertRow(createSequence(start, end));
|
||||
int row = m_model->rowCount() - 1;
|
||||
QModelIndex idx = m_model->index(row, 0);
|
||||
|
||||
if (!idx.isValid())
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("One or more sequences could not be added to the sequences list. <br />Please try again later."),
|
||||
msgTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->setHighlightedRow(row, false /* add, don't reset after adding 1 of n rows */);
|
||||
m_ui->tvSequences->setCurrentIndex(idx);
|
||||
}
|
||||
|
||||
refreshDurationText();
|
||||
m_ui->pbApply->setEnabled(true); // new seq now editable
|
||||
slotChangesMade();
|
||||
}
|
||||
#ifdef AskUserSequenceList
|
||||
);
|
||||
#endif
|
||||
|
||||
});
|
||||
|
||||
return;
|
||||
} // if (seq.pint)
|
||||
|
||||
|
||||
if (seq.start.time_since_epoch().count() == 0 || seq.duration.count() == 0)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Start and end times are not valid. Please check if your input values are valid."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->insertRow(seq);
|
||||
|
||||
int row = m_model->rowCount() - 1;
|
||||
QModelIndex idx = m_model->index(row, 0);
|
||||
|
||||
if (!idx.isValid())
|
||||
return;
|
||||
|
||||
m_model->setHighlightedRow(row);
|
||||
m_ui->tvSequences->setCurrentIndex(idx);
|
||||
|
||||
refreshDurationText();
|
||||
m_ui->pbApply->setEnabled(true); // new seq now editable
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onDeleteSequences()
|
||||
{
|
||||
const auto selRows = m_ui->tvSequences->selectionModel()->selectedRows();
|
||||
QVector<int> rowIds;
|
||||
rowIds.reserve(selRows.size());
|
||||
for (const QModelIndex &idx : selRows)
|
||||
rowIds += idx.row();
|
||||
std::sort(rowIds.begin(), rowIds.end(), std::greater<int>());
|
||||
|
||||
const auto rows = m_model->highlightedRows();
|
||||
int curEditedRow = -1;
|
||||
|
||||
if (rows.count() == 1)
|
||||
curEditedRow = rows.first();
|
||||
|
||||
for (int row : qAsConst(rowIds))
|
||||
{
|
||||
m_model->removeRow(row);
|
||||
if (curEditedRow == row)
|
||||
{
|
||||
curEditedRow = -1;
|
||||
m_model->setHighlightedRow(curEditedRow);
|
||||
m_ui->pbApply->setEnabled(false);
|
||||
onEnaDisa();
|
||||
}
|
||||
if (curEditedRow > row)
|
||||
--curEditedRow;
|
||||
}
|
||||
m_model->setHighlightedRow(curEditedRow);
|
||||
refreshDurationText();
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onSequenceUp()
|
||||
{
|
||||
m_model->moveUp(m_ui->tvSequences->currentIndex());
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onSequenceDown()
|
||||
{
|
||||
m_model->moveDown(m_ui->tvSequences->currentIndex());
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onSetTopicButtonStates(bool enable)
|
||||
{
|
||||
if (enable && m_topicStartTime)
|
||||
m_ui->dtStart->setMsecsSinceEpoch(m_topicStartTime);
|
||||
}
|
||||
|
||||
void TopicEditor::onEnaDisa()
|
||||
{
|
||||
|
||||
const auto selRows = m_ui->tvSequences->selectionModel()->selectedRows();
|
||||
const bool hasSelection = (selRows.size() != 0);
|
||||
const bool hasSingleSelection = (selRows.size() == 1);
|
||||
|
||||
m_ui->pbEditSequence->setEnabled(hasSingleSelection);
|
||||
m_ui->pbDelete->setEnabled(hasSelection);
|
||||
m_ui->pbDown->setEnabled(hasSingleSelection);
|
||||
m_ui->pbUp->setEnabled(hasSingleSelection);
|
||||
}
|
||||
|
||||
void TopicEditor::onMarkerSelected(const QString &description)
|
||||
{
|
||||
m_ui->leDescription->setText(description);
|
||||
}
|
||||
|
||||
} // AudioVideoManagement FileManagement
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Topic Editor for the Situation Topic in the Script Editor
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-04-04
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "avm_sequence_model.h"
|
||||
#include "../avm_playback_menu.h"
|
||||
|
||||
#include <AudioVideoManagement/ava_control.h>
|
||||
#include <AudioVideoManagement/logger.h>
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
|
||||
#include <TopicEditorBase/topic_editor_base.h>
|
||||
|
||||
|
||||
namespace Ui { class AVTopicEditor; }
|
||||
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
class TopicEditor : public TopicEditorBase::TopicEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TopicEditor(LtsSession::LtsSession *session, const TopicEditorBase::TopicData &data, QWidget *parent = nullptr);
|
||||
~TopicEditor();
|
||||
|
||||
private:
|
||||
void updateInternalData() override final;
|
||||
void retranslate() override final;
|
||||
|
||||
AvsSequence createSequence(const AvaTimePoint &start, const AvaTimePoint &end) const;
|
||||
AvsSequence createCurrentSequence() const;
|
||||
void refreshDurationText();
|
||||
void setDurationText(qint64 length);
|
||||
qint64 calcTrackLength(const QJsonArray &arr) const;
|
||||
void playbackDlgNotAvailable(const QString &msg);
|
||||
|
||||
private slots:
|
||||
bool apply2() override final;
|
||||
|
||||
void onEditSequence();
|
||||
void onTimeChanged();
|
||||
void onApplySequence();
|
||||
void onAddSequence();
|
||||
void onDeleteSequences();
|
||||
void onSequenceUp();
|
||||
void onSequenceDown();
|
||||
void onSetTopicButtonStates(bool enable);
|
||||
void onEnaDisa();
|
||||
void onMarkerSelected(const QString &description);
|
||||
|
||||
private:
|
||||
const int s_durationLimit{86400000};
|
||||
|
||||
LtsSession::LtsSession *m_session{nullptr};
|
||||
std::shared_ptr<AudioVideoManagement::DataInterface> m_avmDataIfc{nullptr};
|
||||
|
||||
AvmSequenceModel *m_model{nullptr};
|
||||
AvmSequenceProxyModel *m_proxyModel{nullptr};
|
||||
AvmPlaybackMenu *m_playbackMenu{nullptr};
|
||||
|
||||
bool m_isAudio{false};
|
||||
qint64 m_topicStartTime{0};
|
||||
QString m_topicStreamName;
|
||||
|
||||
Ui::AVTopicEditor *m_ui{nullptr};
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AVTopicEditor</class>
|
||||
<widget class="QWidget" name="AVTopicEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1150</width>
|
||||
<height>577</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Script Editor</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1" rowspan="2">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbxSequences">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sequences:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lbxTopicDuration">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Topic duration:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblDuration">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="3">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="3">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QToolButton" name="pbDown">
|
||||
<property name="toolTip">
|
||||
<string>Sequence Down</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/sort-down.svg</normaloff>:/style/sort-down.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QToolButton" name="pbUp">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/sort-up.svg</normaloff>:/style/sort-up.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="pbDelete">
|
||||
<property name="toolTip">
|
||||
<string>Delete selected sequence from list</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete sequence</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/delete.svg</normaloff>:/style/delete.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QPushButton" name="pbEditSequence">
|
||||
<property name="toolTip">
|
||||
<string>Edit selected sequence from list in the right side of the layout</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit sequence</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/document-edit.svg</normaloff>:/style/document-edit.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="4" colspan="3">
|
||||
<widget class="QTableView" name="tvSequences">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="TopicEditorBase::TopicPreviewWidget" name="widget" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="2" rowspan="2">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Edit / Create sequence</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0,0,0">
|
||||
<item>
|
||||
<widget class="QWidget" name="playbackParent" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbxStrean">
|
||||
<property name="text">
|
||||
<string>Stream:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QLabel" name="lbxEndTime">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>End Time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="8">
|
||||
<widget class="QPushButton" name="pbPreview">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set as current time</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="lbxDescription">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Description:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbxStartTime">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start Time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="6">
|
||||
<widget class="LtsWidgets::LtsMSecDateTimeEdit" name="dtEnd"/>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QLabel" name="lblStream">
|
||||
<property name="text">
|
||||
<string notr="true">-</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="7">
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="5" colspan="4">
|
||||
<widget class="QLineEdit" name="leDescription"/>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<spacer name="horizontalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="LtsWidgets::LtsMSecDateTimeEdit" name="dtStart"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbApply">
|
||||
<property name="text">
|
||||
<string>Apply changes to sequence in list</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/dialog-ok-apply.svg</normaloff>:/style/dialog-ok-apply.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbAdd">
|
||||
<property name="text">
|
||||
<string>Append as new sequence to list</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/document-new.svg</normaloff>:/style/document-new.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TopicEditorBase::TopicPreviewWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>TopicEditorBase/topic_preview_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LtsWidgets::LtsMSecDateTimeEdit</class>
|
||||
<extends>QDateTimeEdit</extends>
|
||||
<header>LtsWidgets/lts_msec_date_time_edit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>tvSequences</tabstop>
|
||||
<tabstop>pbUp</tabstop>
|
||||
<tabstop>pbDown</tabstop>
|
||||
<tabstop>pbDelete</tabstop>
|
||||
<tabstop>pbEditSequence</tabstop>
|
||||
<tabstop>leDescription</tabstop>
|
||||
<tabstop>pbPreview</tabstop>
|
||||
<tabstop>pbApply</tabstop>
|
||||
<tabstop>pbAdd</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Reference in new issue
Block a user