Adds initial intermediate qt6 porting state

This commit is contained in:
torsten committed 2026-08-17 17:54:56 +02:00
1 parent 99136e9460
commit d176b5e0d7
11247 files changed
+1891537

No files matched your search

@@ -0,0 +1,285 @@
#include "audio_settings_avail_streams_model.h"
#include <nglwidgets/ngltreeitem.h>
#include <QFont>
namespace AudioVideoManagement
{
QDataStream &operator<<(QDataStream &ds, const StreamData &sd)
{
ds << sd.topicId << sd.name << sd.bIsCnr;
return ds;
}
QDataStream &operator>>(QDataStream &ds, StreamData &sd)
{
ds >> sd.topicId >> sd.name >> sd.bIsCnr ;
return ds;
}
AvailStreamsModel::AvailStreamsModel(Interface::Dtc *avmDtc,
int32_t exerciseId,
QObject *parent)
: NGLTreeModel(parent, new NGLTreeItem(QVariant()), {tr("Available")})
, m_exerciseId(exerciseId)
, m_avmDtc(avmDtc)
, m_cnrRoot(new NGLTreeItem(QVariant()))
, m_tvcRoot(new NGLTreeItem(QVariant()))
, m_changes(false)
, m_logger(getLogger("AudioSettingsAvailStreamsModel"))
{
Q_ASSERT(avmDtc);
qRegisterMetaTypeStreamOperators<StreamData>();
setMimeType(StreamData::mimeType());
StreamData data;
data.name = tr("CNR");
data.bIsCnr = true;
m_cnrRoot->setData(QVariant::fromValue(data));
m_rootItem->appendChild(m_cnrRoot);
data.name = tr("TVC");
data.bIsCnr = false;
m_tvcRoot->setData(QVariant::fromValue(data));
m_rootItem->appendChild(m_tvcRoot);
}
void AvailStreamsModel::setStreamChanged(const QVector<StreamData> &streams)
{
for (const StreamData &d : streams)
if (!m_changedStreams.contains(QPair<bool,int>(d.bIsCnr, d.topicId)))
m_changedStreams += QPair<bool,int>(d.bIsCnr, d.topicId);
emit modelDataChanged();
}
bool AvailStreamsModel::initModel()
{
connect(m_avmDtc, &AudioVideoManagement::Interface::Dtc::cnrConfigChanged,
this, &AvailStreamsModel::onCnrConfigChanged, Qt::UniqueConnection);
m_avmDtc->cnrConfigs([this](const AudioVideoManagement::CnrConfig & ch) { onCnrConfigChanged(ch, DataModification::eAdded); });
#if 0
AudioVideoManagement::CnrConfig data;
data.exerciseId(42);
data.id(1);
data.radioCircuit(QStringLiteral("test1").toStdString());
onCnrConfigChanged(data, DataModification::eAdded);
data.id(2);
data.radioCircuit(QStringLiteral("test2").toStdString());
onCnrConfigChanged(data, DataModification::eAdded);
AudioVideoManagement::TvcConfig data2;
data2.id(1);
data2.radioCircuit(QStringLiteral("test3 (tvc)").toStdString());
onTvcConfigChanged(data2, DataModification::eAdded);
#endif
connect(m_avmDtc, &AudioVideoManagement::Interface::Dtc::tvcConfigChanged,
this, &AvailStreamsModel::onTvcConfigChanged, Qt::UniqueConnection);
m_avmDtc->tvcConfigs([this](const AudioVideoManagement::TvcConfig & ch) { onTvcConfigChanged(ch, DataModification::eAdded); });
return true;
}
Qt::ItemFlags AvailStreamsModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags f = NGLTreeModel::flags(index) | Qt::ItemIsDropEnabled;
if (!index.parent().isValid())
f &= ~Qt::ItemIsDragEnabled;
return f;
}
QVariant AvailStreamsModel::data(const QModelIndex &index, int role) const
{
switch (role)
{
case Qt::DisplayRole:
{
NGLTreeItem *item = privateData(index);
StreamData data = item->data().value<StreamData>();
DEBUG_LOG(m_logger) << data.name;
return data.name;
}
case Qt::FontRole:
{
NGLTreeItem *item = privateData(index);
StreamData data = item->data().value<StreamData>();
if (!m_changedStreams.contains(QPair<bool,int>(data.bIsCnr, data.topicId)))
return {};
QFont f;
f.setBold(true);
return f;
}
default:
{
break;
}
}
return NGLTreeModel::data(index, role);
}
bool AvailStreamsModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(data);
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
if (action != supportedDropActions())
return false;
return true;
}
bool AvailStreamsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
Q_UNUSED(action);
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
const QVector<NGLTreeItem *> droppedItems = getMimeDataItems(data, m_mimeType);
QVector<StreamData> streams;
streams.reserve(droppedItems.size());
for (const NGLTreeItem *item : droppedItems)
streams += item->data().value<StreamData>();
qDeleteAll(droppedItems);
emit droppedStreamNames(streams);
m_changes = true;
return true;
}
void AvailStreamsModel::onCnrConfigChanged(const AudioVideoManagement::CnrConfig &data, DataModification modType)
{
insertUpdateData(data, modType, m_cnrRoot);
}
void AvailStreamsModel::onTvcConfigChanged(const AudioVideoManagement::TvcConfig &data, DataModification modType)
{
insertUpdateData(data, modType, m_tvcRoot);
}
template <typename T>
void AvailStreamsModel::insertUpdateData(const T &config, DataModification modType, NGLTreeItem *parent)
{
DataModification action = modType;
if (modType == DataModification::eModified)
{
if (config.exerciseId().is_set() && config.exerciseId().get() != m_exerciseId)
action = DataModification::eRemoved;
}
StreamData newData(config);
switch (action)
{
case DataModification::eAdded:
if (config.exerciseId().is_set() && config.exerciseId().get() != m_exerciseId)
break;
Q_FALLTHROUGH();
case DataModification::eModified:
{
bool bFound = false;
for (int i = 0; i < parent->childCount(); ++i)
{
NGLTreeItem *child = parent->child(i);
StreamData data = child->data().value<StreamData>();
if (data == newData)
{
bFound = true;
setItemData(indexFromItem(child), QVariant::fromValue(newData), child->icon());
emit updateEntry(newData);
break;
}
}
if (!bFound)
{
NGLTreeItem *child = new NGLTreeItem(QVariant::fromValue(newData));
insertRow(parent->childCount(), indexFromItem(parent), child);
}
break;
}
case DataModification::eRemoved:
{
for (int i = 0; i < parent->childCount(); ++i)
{
NGLTreeItem *child = parent->child(i);
StreamData data = child->data().value<StreamData>();
if (data == newData)
{
removeRows(i, 1, indexFromItem(parent));
emit removeEntry(newData);
break;
}
}
}
}
}
AvailStreamsFilter::AvailStreamsFilter(AvailStreamsModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
, m_sourceModel(sourceModel)
{
Q_ASSERT(sourceModel);
setSourceModel(m_sourceModel);
}
void AvailStreamsFilter::addAllocatedStreams(const QVector<StreamData> &streams)
{
m_allocatedStreams.insert(streams.begin(), streams.end());
invalidateFilter();
}
void AvailStreamsFilter::setAllocatedStreams(const QVector<StreamData> &streams)
{
m_allocatedStreams.clear();
m_allocatedStreams.insert(streams.begin(), streams.end());
invalidateFilter();
}
void AvailStreamsFilter::removeAllocatedStreams(const QVector<StreamData> &streams)
{
for (const StreamData &stream : streams)
m_allocatedStreams.erase(stream);
invalidateFilter();
}
bool AvailStreamsFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if (!m_sourceModel || !source_parent.isValid())
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
NGLTreeItem *parent = m_sourceModel->privateData(source_parent);
if (!parent)
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
NGLTreeItem *child = parent->child(source_row);
if (!child)
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
StreamData data = child->data().value<StreamData>();
return m_allocatedStreams.count(data) == 0;
}
}
@@ -0,0 +1,82 @@
#pragma once
#include <nglwidgets/ngltreemodel.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/logger.h>
#include <QSortFilterProxyModel>
#include <QVector>
#include "audio_stream_type.h"
#include <set>
#include <utility>
namespace AudioVideoManagement
{
class AvailStreamsModel final : public NGLTreeModel
{
Q_OBJECT
public:
explicit AvailStreamsModel(AudioVideoManagement::Interface::Dtc *avmDtc, int32_t exerciseId,
QObject *parent = nullptr);
~AvailStreamsModel() = default;
bool getChangeState() {bool result = m_changes; m_changes = false; return result;}
void resetChangeState() {m_changes = false;m_changedStreams.clear();}
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role) const override;
Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }
Qt::DropActions supportedDragActions() const override { return Qt::CopyAction; }
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction, int, int, const QModelIndex &) override;
void setStreamChanged(const QVector<StreamData> &streams);
bool initModel();
signals:
void droppedStreamNames(const QVector<AudioVideoManagement::StreamData> &streamNamesDropped);
void updateEntry(const AudioVideoManagement::StreamData &stream);
void removeEntry(const AudioVideoManagement::StreamData &stream);
private slots:
void onCnrConfigChanged(const AudioVideoManagement::CnrConfig &data, DataModification modType);
void onTvcConfigChanged(const AudioVideoManagement::TvcConfig &data, DataModification modType);
private:
template <typename T>
void insertUpdateData(const T &data, DataModification modType, NGLTreeItem *parent);
int32_t m_exerciseId;
AudioVideoManagement::Interface::Dtc *m_avmDtc;
NGLTreeItem *m_cnrRoot;
NGLTreeItem *m_tvcRoot;
bool m_changes;
QVector<QPair<bool , int> > m_changedStreams;
NGL::INGLLogger &m_logger;
};
class AvailStreamsFilter final : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit AvailStreamsFilter(AvailStreamsModel *sourceModel, QObject *parent);
void addAllocatedStreams(const QVector<StreamData> &streams);
void setAllocatedStreams(const QVector<StreamData> &streams);
void removeAllocatedStreams(const QVector<StreamData> &streams);
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
using QSortFilterProxyModel::setSourceModel;
AvailStreamsModel *m_sourceModel;
std::set<StreamData> m_allocatedStreams;
};
}
@@ -0,0 +1,434 @@
#include "audio_settings_dialog.h"
#include "ui_audio_settings_dialog.h"
#include "audio_settings_avail_streams_model.h"
#include "audio_settings_used_streams_model.h"
#include "audio_stream_type.h"
#include <LtsWidgets/lts_message_dialog.h>
#include <LtsSession/lts_session_data_dds.h>
#include <LtsSession/lts_session_data.h>
#include <AudioVideoManagement/avm_ftc_types.h>
namespace AudioVideoManagement
{
AudioSettingsDialog::AudioSettingsDialog(QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_ui(new Ui::AudioSettingsDialog)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_logger(getLogger("AudioSettingsDialog"))
{
m_ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
setOnlineHelpFilename(QStringLiteral("Situation-Voice_Communication_Access"));
}
AudioSettingsDialog::~AudioSettingsDialog()
{
delete m_ui;
}
bool AudioSettingsDialog::init(OperatorManagement::Interface::Dtc *opmDtc,
Interface::Dtc *avmDtc,
Interface::Ftc *avmFtc,
LtsSession::LtsSessionDataDds *session)
{
Q_ASSERT(opmDtc);
Q_ASSERT(avmDtc);
Q_ASSERT(avmFtc);
Q_ASSERT(session);
setUnchangeable(true);
m_session = session;
m_avmDtc = avmDtc;
m_avmFtc = avmFtc;
m_modelAvailable = new AvailStreamsModel(avmDtc, m_session->exerciseId(), this);
if (!m_modelAvailable->initModel())
return false;
m_filterAvailable = new AvailStreamsFilter(m_modelAvailable, this);
m_modelAllocated = new AllocatedStreamsModel(this);
connect(m_modelAvailable, &AvailStreamsModel::droppedStreamNames, this, &AudioSettingsDialog::onDroppedInAvailable);
connect(m_modelAllocated, &AllocatedStreamsModel::droppedStreamNames, this, &AudioSettingsDialog::onDroppedInAllocated);
connect(m_modelAvailable, &AvailStreamsModel::updateEntry, m_modelAllocated, &AllocatedStreamsModel::updateEntry);
connect(m_modelAvailable, &AvailStreamsModel::removeEntry, this, &AudioSettingsDialog::onEntryRemoved);
m_ui->treeViewAvailable->setModel(m_filterAvailable);
m_ui->treeViewAvailable->expandAll();
m_ui->treeViewAllocated->setModel(m_modelAllocated);
connect(opmDtc, &OperatorManagement::Interface::Dtc::userChanged,
this, &AudioSettingsDialog::onOperatorChanged);
auto cbOperator = [this](const OperatorManagement::User & u)
{
onOperatorChanged(u, DataModification::eAdded);
};
opmDtc->users(cbOperator);
fillOperatorBox();
connect(m_avmDtc, &Interface::Dtc::cnrTvcProfileChanged,
this, &AudioSettingsDialog::onCnrTvcProfileChanged);
auto cbRadioCircuits = [this](const CnrTvcProfile & rc)
{
onCnrTvcProfileChanged(rc, DataModification::eAdded);
};
m_avmDtc->cnrTvcProfiles(cbRadioCircuits);
connect(m_ui->pushButtonUp, &QPushButton::clicked, this, &AudioSettingsDialog::onUpButtonClicked);
connect(m_ui->pushButtonDown, &QPushButton::clicked, this, &AudioSettingsDialog::onDownButtonClicked);
connect(m_ui->operatorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AudioSettingsDialog::onSelectionOperatorChanged);
connect(m_ui->treeViewAllocated->selectionModel(), &QItemSelectionModel::selectionChanged, this, &AudioSettingsDialog::onSelectionChangedAllocation);
m_ui->treeViewAllocated->setSelectionMode(QAbstractItemView::MultiSelection);
m_ui->treeViewAvailable->setSelectionMode(QAbstractItemView::MultiSelection);
m_ui->treeViewAllocated->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_ui->treeViewAvailable->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_ui->pushButtonUp->setEnabled(false);
m_ui->pushButtonDown->setEnabled(false);
m_avmFtc->enableAll();
int opId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
if (m_ui->operatorComboBox->currentIndex() != m_ui->operatorComboBox->findData(opId))
m_ui->operatorComboBox->setCurrentIndex(m_ui->operatorComboBox->findData(opId));
else
onSelectionOperatorChanged(0);
setUnchangeable(false);
return true;
}
void AudioSettingsDialog::retranslate()
{
translate(m_ui, tr("Voice Communication Access"));
}
void AudioSettingsDialog::onOperatorChanged(const OperatorManagement::User &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "************** onOperatorChanged ";
switch (modType)
{
case DataModification::eRemoved:
{
Q_ASSERT(m_operatorMap.contains(data.id()));
if (m_ui->operatorComboBox->currentData().toInt() == data.id())
{
__unchange__();
m_modelAllocated->resetChangeState();
m_modelAvailable->resetChangeState();
m_filterAvailable->invalidate();
}
m_operatorMap.remove(data.id());
m_ui->operatorComboBox->removeItem(m_ui->operatorComboBox->findData(data.id()));
break;
}
case DataModification::eAdded:
{
Q_ASSERT(!m_operatorMap.contains(data.id()));
const QString operatorName = QString::fromStdString(data.name());
m_operatorMap.insert(data.id(), operatorName);
m_ui->operatorComboBox->addItem(operatorName, data.id());
m_ui->operatorComboBox->model()->sort(0);
break;
}
case DataModification::eModified:
{
Q_ASSERT(m_operatorMap.contains(data.id()));
const QString operatorName = QString::fromStdString(data.name());
m_operatorMap.insert(data.id(), operatorName);
int i = m_ui->operatorComboBox->findData(data.id());
Q_ASSERT(i != -1);
m_ui->operatorComboBox->setItemText(i, operatorName);
m_ui->operatorComboBox->model()->sort(0);
break;
}
}
}
void AudioSettingsDialog::onCnrTvcProfileChanged(const CnrTvcProfile &data, DataModification modType)
{
if (m_session->exerciseId() == data.exerciseId())
{
if (modType == DataModification::eRemoved)
{
m_cnrTvcProfileMap.remove(data.id());
}
else
{
m_cnrTvcProfileMap.insert(data.id(), data);
fillOperatorList();
}
}
}
void AudioSettingsDialog::fillOperatorList()
{
QVector<StreamData> newStreamData;
for (const auto &rc : qAsConst(m_cnrTvcProfileMap))
{
if ((rc.exerciseId() != m_session->exerciseId())
|| (rc.operatorId() != m_ui->operatorComboBox->currentData().toInt()))
continue;
newStreamData.reserve(rc.data().size());
for (const CnrTvcProfileData &profileEntry : rc.data())
newStreamData += toStreamData(profileEntry);
break;
}
m_modelAllocated->setNewStreamData(newStreamData);
m_filterAvailable->setAllocatedStreams(newStreamData);
}
void AudioSettingsDialog::fillOperatorBox()
{
m_ui->operatorComboBox->clear();
QMap<int, QString>::iterator i = m_operatorMap.begin();
while (i != m_operatorMap.end())
{
m_ui->operatorComboBox->addItem(i.value(), i.key());
++i;
}
m_ui->operatorComboBox->model()->sort(0);
}
void AudioSettingsDialog::onUpButtonClicked()
{
const QModelIndexList mil = m_ui->treeViewAllocated->selectionModel()->selectedIndexes();
if (mil.isEmpty())
return;
m_modelAllocated->changeOrder(mil.first(), true);
if (mil.first().row() > 0)
{
const QModelIndex newRow = m_modelAllocated->index(mil.first().row() - 1, 0);
m_ui->treeViewAllocated->setCurrentIndex(newRow);
}
slotChangesMade();
}
void AudioSettingsDialog::onDownButtonClicked()
{
const QModelIndexList mil = m_ui->treeViewAllocated->selectionModel()->selectedIndexes();
if (mil.isEmpty())
return;
m_modelAllocated->changeOrder(mil.first(), false);
const QModelIndex newRow = m_modelAllocated->index(mil.first().row() + 1, 0);
m_ui->treeViewAllocated->setCurrentIndex(newRow);
slotChangesMade();
}
void AudioSettingsDialog::onSelectionChangedAllocation(const QItemSelection &selected, const QItemSelection &deselected)
{
DEBUG_LOG(m_logger) << "onSelectionChangedAllocation selected.isEmpty()=" << selected.isEmpty();
Q_UNUSED(deselected);
QModelIndex first = m_ui->treeViewAllocated->model()->index(0, 0);
int rowCnt = m_ui->treeViewAllocated->model()->rowCount();
QModelIndex last = m_ui->treeViewAllocated->model()->index(rowCnt - 1, 0);
QModelIndexList selectedRows = m_ui->treeViewAllocated->selectionModel()->selectedIndexes();
const int cntSelectedRows = selectedRows.size();
if (cntSelectedRows > 1)
{
m_ui->pushButtonUp->setEnabled(false);
m_ui->pushButtonDown->setEnabled(false);
return;
}
if (selected.size() > 0)
{
m_ui->pushButtonUp->setEnabled(selected.first().indexes().first() != first);
m_ui->pushButtonDown->setEnabled(selected.first().indexes().first() != last);
}
}
bool AudioSettingsDialog::apply()
{
if (m_modelAllocated->getStreamDataVector().isEmpty())
applyRemove();
else
applyModify();
m_modelAllocated->resetChangeState();
m_modelAvailable->resetChangeState();
m_filterAvailable->invalidate();
return true;
}
bool AudioSettingsDialog::applyRemove()
{
RemoveCnrTvcProfileRequest removeRequest;
DEBUG_LOG(m_logger) << "AudioSettingsDialog::applyRemove";
for (const auto &profile : qAsConst(m_cnrTvcProfileMap))
{
if (profile.operatorId() != m_operatorId)
continue;
removeRequest.id(profile.id());
break;
}
if (removeRequest.id() <= 0)
return true;
m_requestHelper->startSingleRequest([this, removeRequest]()
{
return m_avmFtc->removeCnrTvcProfileRequest(removeRequest, this);
});
return true;
}
bool AudioSettingsDialog::applyModify()
{
ModifyCnrTvcProfileRequest modifyRequest;
DEBUG_LOG(m_logger) << "AudioSettingsDialog::applyModify";
for (const auto &profile : qAsConst(m_cnrTvcProfileMap))
{
if (profile.operatorId() != m_operatorId)
continue;
modifyRequest.id(profile.id());
break;
}
modifyRequest.exerciseId(m_session->exerciseId());
modifyRequest.operatorId(m_operatorId);
const QVector<StreamData> allocatedList = m_modelAllocated->getStreamDataVector();
modifyRequest.data().reserve(allocatedList.size());
for (const StreamData &stream : allocatedList)
{
CnrTvcProfileData circuitData;
circuitData.radioSystemId(stream.bIsCnr ? RadioSystem::Cnr : RadioSystem::Tvc);
circuitData.radioCircuitId(stream.topicId);
modifyRequest.data().push_back(std::move(circuitData));
}
m_requestHelper->startSingleRequest([this, modifyRequest]()
{
return m_avmFtc->modifyCnrTvcProfileRequest(modifyRequest, this);
});
return true;
}
void AudioSettingsDialog::onModifyCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject)
{
__unchange__();
m_requestHelper->requestReply(replyObject);
}
void AudioSettingsDialog::onRemoveCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject)
{
__unchange__();
m_requestHelper->requestReply(replyObject);
}
void AudioSettingsDialog::onSelectionOperatorChanged(const int index)
{
Q_UNUSED(index);
if (hasUnsavedChanges() ||
m_modelAllocated->getChangeState() ||
m_modelAvailable->getChangeState())
{
switch (askApplyDiscardCancel())
{
case LtsWidgets::Button::Apply:
if (!apply())
return;
break;
case LtsWidgets::Button::Cancel:
{
{
QSignalBlocker blockSignals(m_ui->operatorComboBox);
m_ui->operatorComboBox->setCurrentIndex(m_ui->operatorComboBox->findData(m_operatorId));
}
return;
}
case LtsWidgets::Button::Discard:
__unchange__();
break;
default:
break;
}
}
m_ui->pushButtonUp->setEnabled(false);
m_ui->pushButtonDown->setEnabled(false);
m_operatorId = m_ui->operatorComboBox->currentData().toInt();
DEBUG_LOG(m_logger) << "onSelectionOperatorChanged" << " operatorStr=" << m_ui->operatorComboBox->currentText() << " m_operatorId= " << m_operatorId;
fillOperatorList();
m_modelAllocated->resetChangeState();
m_modelAvailable->resetChangeState();
m_filterAvailable->invalidate();
}
void AudioSettingsDialog::onDroppedInAllocated(const QVector<StreamData> &streams)
{
slotChangesMade();
m_filterAvailable->addAllocatedStreams(streams);
m_modelAllocated->setStreamChanged(streams);
m_modelAvailable->setStreamChanged(streams);
}
void AudioSettingsDialog::onDroppedInAvailable(const QVector<StreamData> &streams)
{
slotChangesMade();
m_filterAvailable->removeAllocatedStreams(streams);
m_modelAllocated->setStreamChanged(streams);
m_modelAvailable->setStreamChanged(streams);
}
void AudioSettingsDialog::onEntryRemoved(const StreamData &stream)
{
m_modelAllocated->removeEntry(stream);
m_filterAvailable->removeAllocatedStreams({stream});
}
StreamData AudioSettingsDialog::toStreamData(const CnrTvcProfileData &profileEntry) const
{
int configId = profileEntry.radioCircuitId();
StreamData streamData;
streamData.topicId = configId;
if (profileEntry.radioSystemId() == RadioSystem::Cnr)
{
streamData.name = QString::fromStdString(m_avmDtc->cnrConfig(configId).radioCircuit());
streamData.bIsCnr = true;
}
else
{
streamData.name = QString::fromStdString(m_avmDtc->tvcConfig(configId).radioCircuit());
streamData.bIsCnr = false;
}
return streamData;
}
}
@@ -0,0 +1,102 @@
#pragma once
#include <OperatorManagement/opm_dtc.h>
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/logger.h>
#include <LtsWidgets/lts_dialog.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <AudioVideoManagement/avm_ftc.h>
#include <QVector>
#include <QMap>
#include <QString>
namespace Ui
{
class AudioSettingsDialog;
}
namespace LtsSession
{
class LtsSessionDataDds;
}
class QItemSelection;
namespace AudioVideoManagement
{
struct StreamData;
class AvailStreamsModel;
class AvailStreamsFilter;
class AllocatedStreamsModel;
class AudioSettingsDialog : public LtsWidgets::LtsDialog
, protected AudioVideoManagement::Interface::Ftc::ModifyCnrTvcProfileReplyHandler
, protected AudioVideoManagement::Interface::Ftc::RemoveCnrTvcProfileReplyHandler
{
Q_OBJECT
public:
explicit AudioSettingsDialog(QWidget *parent = nullptr);
~AudioSettingsDialog();
bool init(OperatorManagement::Interface::Dtc *opmDtc,
AudioVideoManagement::Interface::Dtc *avmDtc,
AudioVideoManagement::Interface::Ftc *avmFtc,
LtsSession::LtsSessionDataDds *session);
void onModifyCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject) override;
void onRemoveCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject) override;
private:
enum class CircuitChoice
{
None,
Cnr,
Tvc
};
void retranslate() override;
bool apply() override final;
bool applyModify();
bool applyRemove();
void fillOperatorBox();
void fillOperatorList();
private slots:
void onOperatorChanged(const OperatorManagement::User &data, DataModification modType);
void onCnrTvcProfileChanged(const AudioVideoManagement::CnrTvcProfile &data, DataModification modType);
void onUpButtonClicked();
void onDownButtonClicked();
void onSelectionChangedAllocation(const QItemSelection &selected, const QItemSelection &deselected);
void onSelectionOperatorChanged(const int index);
void onDroppedInAllocated(const QVector<StreamData> &streams);
void onDroppedInAvailable(const QVector<StreamData> &streams);
void onEntryRemoved(const StreamData &stream);
private:
StreamData toStreamData(const CnrTvcProfileData &profileEntry) const;
AudioVideoManagement::Interface::Dtc *m_avmDtc{nullptr};
AudioVideoManagement::Interface::Ftc *m_avmFtc{nullptr};
int m_operatorId{-1};
QMap<int, QString> m_operatorMap;
QMap<int, AudioVideoManagement::CnrTvcProfile> m_cnrTvcProfileMap;
AvailStreamsModel *m_modelAvailable{nullptr};
AvailStreamsFilter *m_filterAvailable{nullptr};
AllocatedStreamsModel *m_modelAllocated{nullptr};
Ui::AudioSettingsDialog *m_ui{nullptr};
LtsUtils::RequestHelper *m_requestHelper{nullptr};
LtsSession::LtsSessionDataDds *m_session{nullptr};
LOGGER_T m_logger;
};
}
@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioSettingsDialog</class>
<widget class="QWidget" name="AudioSettingsDialog">
<property name="windowTitle">
<string>Voice Communication Access</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" rowspan="5">
<widget class="QTreeView" name="treeViewAvailable">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
</widget>
</item>
<item row="1" column="1" rowspan="5">
<widget class="QTreeView" name="treeViewAllocated">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::CopyAction</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<attribute name="headerCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="1" column="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>90</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="pushButtonUp">
<property name="text">
<string notr="true">↑ </string>
</property>
</widget>
</item>
<item row="3" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="pushButtonDown">
<property name="text">
<string notr="true">↓</string>
</property>
</widget>
</item>
<item row="5" column="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>89</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="3">
<spacer name="horizontalSpacer">
<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 row="0" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lbxOperator">
<property name="text">
<string>Operator:</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsComboBox" name="operatorComboBox">
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<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>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>operatorComboBox</tabstop>
<tabstop>treeViewAvailable</tabstop>
<tabstop>treeViewAllocated</tabstop>
<tabstop>pushButtonUp</tabstop>
<tabstop>pushButtonDown</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,180 @@
#include "audio_settings_used_streams_model.h"
#include <nglwidgets/ngltreeitem.h>
#include <QMimeData>
#include <QFont>
namespace AudioVideoManagement
{
AllocatedStreamsModel::AllocatedStreamsModel(QObject *parent)
: NGLTreeModel(parent, new NGLTreeItem(QVariant(QLatin1String("root"))), { tr("Allocated") })
, m_logger(getLogger("AudioSettingsUsedStreamsModel"))
{
setMimeType(StreamData::mimeType());
}
Qt::ItemFlags AllocatedStreamsModel::flags(const QModelIndex &index) const
{
return NGLTreeModel::flags(index) | Qt::ItemIsDropEnabled;
}
QVariant AllocatedStreamsModel::data(const QModelIndex &index, int role) const
{
switch (role)
{
case Qt::DisplayRole:
{
NGLTreeItem *item = privateData(index);
StreamData data = item->data().value<StreamData>();
DEBUG_LOG(m_logger) << data.name;
return data.name;
}
case Qt::FontRole:
{
NGLTreeItem *item = privateData(index);
StreamData data = item->data().value<StreamData>();
if (!m_changedStreams.contains(QPair<bool,int>(data.bIsCnr, data.topicId)))
return {};
QFont f;
f.setBold(true);
return f;
}
default:
{
break;
}
}
return NGLTreeModel::data(index, role);
}
void AllocatedStreamsModel::setStreamChanged(const QVector<StreamData> &streams)
{
for (const StreamData &d : streams)
if (!m_changedStreams.contains(QPair<bool,int>(d.bIsCnr, d.topicId)))
m_changedStreams += QPair<bool,int>(d.bIsCnr, d.topicId);
emit modelDataChanged();
}
bool AllocatedStreamsModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(data);
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
if (action != supportedDropActions())
return false;
return true;
}
bool AllocatedStreamsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
const QVector<NGLTreeItem *> droppedItems = getMimeDataItems(data, m_mimeType);
QVector<StreamData> streams;
streams.reserve(droppedItems.size());
for (const NGLTreeItem *item : droppedItems)
streams += item->data().value<StreamData>();
qDeleteAll(droppedItems);
emit droppedStreamNames(streams);
m_changes = true;
DEBUG_LOG(m_logger) << data->data(QStringLiteral("NGLTreeModel/dragDrop"));
return NGLTreeModel::dropMimeData(data, action, row, 0, QModelIndex());
}
void AllocatedStreamsModel::setNewStreamData(const QVector<StreamData> &newData)
{
DEBUG_LOG(m_logger) << "set new stream data, count: " << newData.size();
resetModel();
for (const StreamData &sd : newData)
m_rootItem->appendChild(new NGLTreeItem(QVariant::fromValue(sd)));
emit modelDataChanged();
}
QVector<StreamData> AllocatedStreamsModel::getStreamDataVector() const
{
QVector<StreamData> result;
result.reserve(m_rootItem->childCount());
for (int i = 0; i < m_rootItem->childCount(); i++)
{
const NGLTreeItem *item = m_rootItem->child(i);
const StreamData sd = item->data().value<StreamData>();
result.append(sd);
DEBUG_LOG(m_logger) << "result.append(sd.name):" << sd.name;
}
DEBUG_LOG(m_logger) << "getStreamDataVector() size: " << result.size();
return result;
}
void AllocatedStreamsModel::changeOrder(const QModelIndex &idx, bool up)
{
DEBUG_LOG(m_logger) << "changeOrder "
<< " index = " << idx.row()
<< " up = " << up
<< " m_rootItem->childCount()= " << m_rootItem->childCount();
if (up)
{
moveRow(idx.parent(), idx.row(),
idx.parent(), idx.row() - 1);
}
else
{
moveRow(idx.parent(), idx.row(),
idx.parent(), idx.row() + 1);
}
emit modelDataChanged();
}
void AllocatedStreamsModel::updateEntry(const StreamData &stream)
{
for (int i = 0; i < m_rootItem->childCount(); i++)
{
NGLTreeItem *child = m_rootItem->child(i);
StreamData data = child->data().value<StreamData>();
if (data == stream)
{
setItemData(indexFromItem(child), QVariant::fromValue(stream), child->icon());
break;
}
}
emit modelDataChanged();
}
void AllocatedStreamsModel::removeEntry(const StreamData &stream)
{
for (int i = 0; i < m_rootItem->childCount(); i++)
{
NGLTreeItem *child = m_rootItem->child(i);
StreamData data = child->data().value<StreamData>();
if (data == stream)
{
removeRows(i, 1, indexFromItem(m_rootItem));
break;
}
}
emit modelDataChanged();
}
}
@@ -0,0 +1,48 @@
#pragma once
#include <nglwidgets/ngltreemodel.h>
#include <AudioVideoManagement/logger.h>
#include "audio_stream_type.h"
namespace AudioVideoManagement
{
class AllocatedStreamsModel final : public NGLTreeModel
{
Q_OBJECT
public:
explicit AllocatedStreamsModel(QObject *parent = nullptr);
bool getChangeState() {bool result = m_changes; m_changes = false; return result;}
void resetChangeState() {m_changes = false;m_changedStreams.clear();}
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::DropActions supportedDropActions() const override { return Qt::CopyAction; }
Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; }
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) override;
void setStreamChanged(const QVector<StreamData> &streams);
void setNewStreamData(const QVector<StreamData> &newData);
QVector<StreamData> getStreamDataVector() const;
void changeOrder(const QModelIndex &idx, bool up);
void updateEntry(const StreamData &stream);
void removeEntry(const StreamData &stream);
signals:
void droppedStreamNames(const QVector<AudioVideoManagement::StreamData> &streamNamesDropped);
private:
bool m_changes{false};
QVector<QPair<bool , int> > m_changedStreams;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,47 @@
#pragma once
#include <AudioVideoManagement/avm.hpp>
#include <QString>
#include <QObject>
#include <boost/operators.hpp>
#include <utility>
namespace AudioVideoManagement
{
struct StreamData : boost::totally_ordered<StreamData>
{
StreamData() = default;
explicit StreamData(const TvcConfig &tvcConfig)
: name(QString::fromStdString(tvcConfig.radioCircuit()))
, topicId(tvcConfig.id())
, bIsCnr(false)
{}
explicit StreamData(const CnrConfig &cnrConfig)
: name(QString::fromStdString(cnrConfig.radioCircuit()))
, topicId(cnrConfig.id())
, bIsCnr(true)
{}
QString name;
int topicId = 0;
bool bIsCnr = false;
static QString mimeType() { return QStringLiteral("AVM/StreamData"); }
bool operator<(const StreamData &other) const { return asPair() < other.asPair(); }
bool operator==(const StreamData &other) const { return asPair() == other.asPair(); }
private:
std::pair<bool, int> asPair() const { return {bIsCnr, topicId}; }
};
}
Q_DECLARE_METATYPE(AudioVideoManagement::StreamData);
@@ -0,0 +1,86 @@
#
# $Id$
# $HeadURL$
# $Author$
# Created by Patric Karwat, SLSE, 2016-10-05
#
SET(avm_menu_edit_gui_SRCS
avm_menu_edit_gui_plugin.cpp
avm_menu_edit_gui_plugin.h
avm_menu_edit_view.cpp
avm_menu_edit_view.h
avm_menu_edit.cpp
avm_menu_edit.h
avm_menu_view_model.cpp
avm_menu_view_model.h
avm_available_streams_model.cpp
avm_available_streams_model.h
avm_choose_menu.cpp
avm_choose_menu.h
avm_new_menu_struct.cpp
avm_new_menu_struct.h
avm_used_streams_model.cpp
avm_used_streams_model.h
avm_playback_menu.cpp
avm_playback_menu.h
avm_playback_menu_dialog.cpp
avm_playback_menu_dialog.h
TopicEditor/topic_editor.cpp
TopicEditor/topic_editor.h
TopicEditor/avm_sequence_model.cpp
TopicEditor/avm_sequence_model.h
interval_model.cpp
interval_model.h
topic_replay.cpp
topic_replay.h
AudioSettings/audio_settings_dialog.cpp
AudioSettings/audio_settings_dialog.h
AudioSettings/audio_stream_type.h
AudioSettings/audio_settings_avail_streams_model.cpp
AudioSettings/audio_settings_avail_streams_model.h
AudioSettings/audio_settings_used_streams_model.cpp
AudioSettings/audio_settings_used_streams_model.h
)
NGL_WRAP_UI(avm_menu_edit_gui_SRCS
avm_menu_edit_view.ui
avm_menu_edit.ui
avm_choose_menu.ui
avm_new_menu_struct.ui
avm_playback_menu.ui
avm_playback_menu_dialog.ui
TopicEditor/topic_editor.ui
AudioSettings/audio_settings_dialog.ui
)
NGL_ADD_LIBRARY(WS avmMenuEditGuiPlugin MODULE ${avm_menu_edit_gui_SRCS})
NGL_TARGET_LINK_LIBRARIES(avmMenuEditGuiPlugin
AvmDataPluginInterface
AvmMenuEditGuiPluginInterface
AvmShared
FlmDataPluginInterface
IsmDataPluginInterface
LtsDataView
LtsPrinting
LtsScriptTimeline
LtsSession
LtsSessionData
LtsUtilsGuiDds
LtsUtilsCore
LtsWidgets
LtsUtilsSettings
nglsettings
MrkCommon
MrkDataPluginInterface
NGLLogger
ngltools
nglwidgets
OpmDataPluginInterface
Qt5::Widgets
TopicEditorBase
Boost::boost
)
NGL_INSTALL_TARGET(avmMenuEditGuiPlugin)
@@ -0,0 +1,254 @@
#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)
{
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)
{
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;
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);
}
}
@@ -0,0 +1,80 @@
#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;
};
}
@@ -0,0 +1,677 @@
#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;
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;
}
m_ui->pbApply->setEnabled(false);
m_ui->pbDelete->setEnabled(false);
m_ui->pbUp->setEnabled(false);
m_ui->pbDown->setEnabled(false);
boost::optional<QString> stream = boost::none;
if (m_topicData.properties.contains(QStringLiteral("stream")))
stream = m_topicData.properties.value(QStringLiteral("stream")).toString();
qint64 startMsecs = 0;
boost::optional<AvaTimePoint> start = boost::none;
{
const QJsonValue jsonValue(getTopicDataValue(QStringLiteral("start")));
if (jsonValue.isDouble())
{
startMsecs = static_cast<qint64>(jsonValue.toDouble());
start = boost::optional<AvaTimePoint>(toTimePoint(startMsecs));
}
}
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 ,
stream, start
);
m_ui->playbackParent->layout()->addWidget(m_playbackMenu);
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();
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);
}
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 (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);
m_ui->dtStart->setTimezoneHoursOffset(offset);
m_ui->dtEnd->setTimezoneHoursOffset(offset);
}
TopicEditor::~TopicEditor()
{
delete m_ui;
delete m_model;
}
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, 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;
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 );
}
return length;
}
void TopicEditor::playbackDlgNotAvailable(const QString &msg)
{
new LtsWidgets::LtsMessageDialogNew(this, msg);
m_ui->groupBox->hide();
m_ui->line->hide();
m_ui->pbEditSequence->hide();
}
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))
{
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;
qint64 msecs = duration - (secs * 1000);
qWarning() << "secs" << secs << "msecs" << msecs;
if (msecs >= 500)
++secs;
qWarning() << "secs now" << secs;
m_topicData.duration = secs;
}
else
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());
m_ui->pbApply->setEnabled(true);
}
void TopicEditor::onTimeChanged()
{
}
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)
{
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"));
}
#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);
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 );
m_ui->tvSequences->setCurrentIndex(idx);
}
refreshDurationText();
m_ui->pbApply->setEnabled(true);
slotChangesMade();
}
#ifdef AskUserSequenceList
);
#endif
});
return;
}
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);
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);
}
}
@@ -0,0 +1,73 @@
#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;
};
}
@@ -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>
@@ -0,0 +1,318 @@
#include "avm_available_streams_model.h"
#include <AudioVideoManagement/stream_name_mapping.h>
#include <InfrastructureManagement/ism.hpp>
#include <nglwidgets/ngltreeitem.h>
#include <QSet>
#include <QMimeData>
#include <QCollator>
namespace
{
bool isMountingPointOfInterest(const InfrastructureManagement::StructureElement &se, bool isAudio)
{
if (se.structureElementTypeId() != InfrastructureManagement::StructureElementType::MountingPoint)
return false;
if (se.mountingPointTypeId() == InfrastructureManagement::MountingPointType::IndoorCamera ||
se.mountingPointTypeId() == InfrastructureManagement::MountingPointType::OutdoorCamera)
return true;
if (isAudio && se.mountingPointTypeId() == InfrastructureManagement::MountingPointType::AnnouncementUnit)
return true;
return false;
}
}
namespace AudioVideoManagement
{
const QString AvailableStreamsModel::s_MimeType = QStringLiteral("AvailStreamsModel/DragDrop");
struct sorter
{
bool operator()(const StreamModelData &i, const StreamModelData &j)
{
QCollator col;
col.setNumericMode(true);
return col.compare(j.name, i.name) > 0 ;
}
} sortStuff;
AvailableStreamsModel::AvailableStreamsModel(const std::shared_ptr<Interface::Dtc> &avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
LtsSession::LtsSession *session,
StreamNameMapping *streamNameMapping,
QObject *parent,
bool isAudio)
: NGLTreeModel(parent, new NGLTreeItem(QVariant(), QIcon()), {tr("Available Streams")})
, m_isAudio(isAudio)
, m_avmDtc(avmDtc)
, m_ismDtc(ismDtc)
, m_session(session)
, m_streamNameMapping(streamNameMapping)
, m_logger(getLogger("AvailableStreamsModel"))
{
Q_ASSERT(ismDtc);
Q_ASSERT(streamNameMapping);
qRegisterMetaTypeStreamOperators<StreamModelData>("StreamModelData");
qRegisterMetaType<StreamModelData>("StreamModelData");
setMimeType(s_MimeType);
connect(m_ismDtc.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged,
this, [ this]()
{
initModel();
});
connect(m_streamNameMapping, &StreamNameMapping::changed,
this, [ this]()
{
initModel();
});
if (!m_isAudio)
{
connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::videoTeamChanged,
this, [ this]()
{
initModel();
});
}
initModel();
}
QVariant AvailableStreamsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
NGLTreeItem *item = privateData(index);
if (!item->data().canConvert<StreamModelData>())
return QVariant();
const StreamModelData availStream = item->data().value<StreamModelData>();
switch (role)
{
case Qt::DisplayRole:
{
switch (index.column())
{
case 0 :
return availStream.name;
default:
return QStringLiteral("??");
}
break;
}
case Qt::DecorationRole:
{
switch (index.column())
{
case 0:
{
static const QIcon folder(QStringLiteral("icon:evaluation_folder.svg"));
static const QIcon camera(QStringLiteral("icon:camera_control.svg"));
return availStream.isGroup ? folder : camera;
}
default:
break;
}
break;
}
default:
break;
}
return NGLTreeModel::data(index, role);
}
Qt::ItemFlags AvailableStreamsModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = NGLTreeModel::flags(index);
NGLTreeItem *item = privateData(index);
if (item == m_rootItem)
return defaultFlags;
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
}
QMimeData *AvailableStreamsModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *pData = NGLTreeModel::mimeData(indexes);
pData->setText(data(indexes[0]).toString());
return pData;
}
bool AvailableStreamsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
INFO_LOG(m_logger) << "dropMimeData: action = " << action
<< ", row = " << row
<< ", parent: valid = " << parent.isValid();
if (action == Qt::IgnoreAction)
return true;
if (!data->hasFormat(s_MimeType))
return false;
if ((action != Qt::MoveAction) && (action != Qt::CopyAction))
return false;
return NGLTreeModel::dropMimeData(data, action, row, column, parent);
}
void AvailableStreamsModel::initModel()
{
beginResetModel();
INFO_LOG(m_logger) << "initModel";
m_rootItem->removeAllChildren();
StreamModelData availStream;
availStream.id = 0;
availStream.name = tr("New group");
availStream.parentId = -1;
availStream.isGroup = true;
QVariant data;
data.setValue(availStream);
m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
std::vector<StreamModelData> toAddIndoor;
std::vector<StreamModelData> toAddOutdoor;
std::vector<StreamModelData> toAddMvs;
const auto &mountedAssets = m_ismDtc->mountingPointAssets();
const auto &buildings = m_ismDtc->mainStructureElements();
for (const auto &building : buildings)
{
for (const auto &mountingPoint : building.elements())
{
if (!isMountingPointOfInterest(mountingPoint, m_isAudio))
continue;
bool isMounted = false;
for (const auto &mountedAsset : mountedAssets)
{
if (mountedAsset.structureElementId() == mountingPoint.id())
{
isMounted = true;
break;
}
}
if (!isMounted)
continue;
StreamModelData item;
item.name = m_streamNameMapping->displayName(mountingPoint.id());
item.mountingPointId = mountingPoint.id();
item.isGroup = false;
if (mountingPoint.mountingPointTypeId() == InfrastructureManagement::MountingPointType::IndoorCamera)
toAddIndoor.push_back(item);
else
toAddOutdoor.push_back(item);
}
}
if (!m_isAudio)
{
const auto &mvsStreams = m_avmDtc->videoTeams();
for (const auto &videoTeam : mvsStreams)
{
if (videoTeam.exerciseId() != m_session->exerciseId())
continue;
DEBUG_LOG(m_logger) << "add new videoteam" << videoTeam;
StreamModelData item;
item.name = QString::fromStdString(videoTeam.streamName().c_str());
item.mountingPointId = -1 * videoTeam.id();
item.isGroup = false;
toAddMvs.push_back(item);
}
}
std::sort(toAddOutdoor.begin(), toAddOutdoor.end(), sortStuff);
for (const auto &item : toAddOutdoor)
{
data.setValue(item);
m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
}
std::sort(toAddIndoor.begin(), toAddIndoor.end(), sortStuff);
for (const auto &item : toAddIndoor)
{
data.setValue(item);
m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
}
std::sort(toAddMvs.begin(), toAddMvs.end(), sortStuff);
for (const auto &item : toAddMvs)
{
data.setValue(item);
m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
}
endResetModel();
}
StreamModelData StreamModelData::createFromServiceItem(const AvMenuEntry &entry, StreamNameMapping *streamNameMapping, const QMap<int, VideoTeam> &videoTeamList)
{
StreamModelData data;
data.id = entry.id();
data.parentId = entry.parentId();
data.isGroup = entry.isGroup();
if (entry.isGroup())
data.name = QString::fromStdString(entry.name().get());
else
{
if (entry.mountingPointId().is_set() && (entry.mountingPointId().get() > 0))
{
data.mountingPointId = entry.mountingPointId().get();
data.name = streamNameMapping->displayName(entry.mountingPointId().get());
}
else
{
data.mountingPointId = entry.mountingPointId().get();
data.name = QString::fromStdString(videoTeamList.value(entry.mountingPointId().get() * -1).streamName());
}
}
return data;
}
}
@@ -0,0 +1,104 @@
#pragma once
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm.hpp>
#include <AudioVideoManagement/avm_dtc.h>
#include <LtsSession/lts_session.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <nglwidgets/ngltreemodel.h>
#include <QDataStream>
class NGLTreeItem;
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class StreamNameMapping;
struct StreamModelData
{
qint64 id = 0;
qint64 parentId = 0;
bool isGroup = false;
QString name;
qint32 mountingPointId = 0;
static StreamModelData createFromServiceItem(const AvMenuEntry &entry, StreamNameMapping *streamNameMapping, const QMap<int, VideoTeam> &videoTeamList);
friend QDataStream &operator<<(QDataStream &out,
const StreamModelData &myObj)
{
out << myObj.id
<< myObj.parentId
<< myObj.isGroup
<< myObj.name
<< myObj.mountingPointId;
return out;
}
friend QDataStream &operator>>(QDataStream &in,
StreamModelData &myObj)
{
in >> myObj.id
>> myObj.parentId
>> myObj.isGroup
>> myObj.name
>> myObj.mountingPointId;
return in;
}
};
class AvailableStreamsModel : public NGLTreeModel
{
Q_OBJECT
public:
static const QString s_MimeType;
AvailableStreamsModel(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
LtsSession::LtsSession *session,
StreamNameMapping *streamNameMapping,
QObject *parent,
bool isAudio);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) override;
Qt::DropActions supportedDragActions() const override { return Qt::CopyAction; }
private:
void initModel();
const bool m_isAudio;
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_avmDtc;
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
LtsSession::LtsSession *m_session;
StreamNameMapping *m_streamNameMapping;
NGL::INGLLogger &m_logger;
};
}
Q_DECLARE_METATYPE(AudioVideoManagement::StreamModelData)
@@ -0,0 +1,122 @@
#include "avm_choose_menu.h"
#include "ui_avm_choose_menu.h"
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/avm.hpp>
#include <AudioVideoManagement/avs_menus.h>
#include <LtsUtilsCore/data_modification_enum.h>
#include <LtsSession/lts_session.h>
namespace AudioVideoManagement
{
ChooseMenu::ChooseMenu(Interface::Dtc *dtc, AvsMenus *avsMenus, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_dtc(dtc)
, m_avsMenus(avsMenus)
, m_ui(new Ui::ChooseMenuStruct)
, m_logger(getLogger("ChooseMenu"))
{
Q_ASSERT(dtc);
Q_ASSERT(avsMenus);
m_ui->setupUi(this);
connect(dtc, &Interface::Dtc::menuStructChanged, this, &ChooseMenu::onMenuStructChanged);
m_dtc->menuStructs([this](const MenuStruct & menu) { onMenuStructChanged(menu, DataModification::eAdded);});
selectCurrent();
connect(m_ui->cbAudio, &QComboBox::currentTextChanged, this, &ChooseMenu::slotChangesMade);
connect(m_ui->cbVideo, &QComboBox::currentTextChanged, this, &ChooseMenu::slotChangesMade);
setOnlineHelpFilename(QStringLiteral("Situation-Choose_AV_Menu"));
}
ChooseMenu::~ChooseMenu()
{
delete m_ui;
}
void ChooseMenu::retranslate()
{
translate(m_ui, tr("Choose AV Menu"));
}
void ChooseMenu::onMenuStructChanged(const MenuStruct &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
{
if (data.isAudio())
m_ui->cbAudio->addItem(QString::fromStdString(data.name()), data.id());
else
m_ui->cbVideo->addItem(QString::fromStdString(data.name()), data.id());
break;
}
case DataModification::eRemoved:
{
if (data.isAudio())
{
int index = m_ui->cbAudio->findData(data.id());
m_ui->cbAudio->removeItem(index);
}
else
{
int index = m_ui->cbVideo->findData(data.id());
m_ui->cbVideo->removeItem(index);
}
break;
}
case DataModification::eModified:
break;
}
}
bool ChooseMenu::apply()
{
int menuId = m_ui->cbAudio->currentData().toInt();
INFO_LOG(m_logger) << "MenuId Audio: " << menuId;
m_avsMenus->setAnnouncementMenu(menuId);
menuId = m_ui->cbVideo->currentData().toInt();
INFO_LOG(m_logger) << "MenuId Video: " << menuId;
m_avsMenus->setVideoMenu(menuId);
return true;
}
void ChooseMenu::selectCurrent()
{
selectMenu(m_avsMenus->currentAnnouncementMenu(), m_ui->cbAudio);
selectMenu(m_avsMenus->currentVideoMenu(), m_ui->cbVideo);
}
void ChooseMenu::selectMenu(int menuId, QComboBox *cb)
{
Q_ASSERT(cb);
if (menuId < 0)
return;
int index = cb->findData(menuId);
if (index < 0)
return;
cb->setCurrentIndex(index);
}
}
@@ -0,0 +1,56 @@
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <AudioVideoManagement/logger.h>
enum class DataModification;
namespace Ui
{
class ChooseMenuStruct;
}
namespace LtsSession
{
class LtsSession;
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
}
class MenuStruct;
class AvsMenus;
class ChooseMenu : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
ChooseMenu(Interface::Dtc *dtc, AvsMenus *avsMenus, QWidget *parent = nullptr);
~ChooseMenu() override;
private slots:
void onMenuStructChanged(const MenuStruct &data, DataModification modType);
private:
void retranslate() override;
bool apply() override final;
void selectCurrent();
void selectMenu(int menuId, QComboBox *cb);
Interface::Dtc *m_dtc;
AvsMenus *m_avsMenus;
Ui::ChooseMenuStruct *m_ui;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ChooseMenuStruct</class>
<widget class="QWidget" name="ChooseMenuStruct">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>596</width>
<height>102</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Choose Menu</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Choose Audio Menu:</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsComboBox" name="cbAudio"/>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<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>
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="LtsWidgets::LtsComboBox" name="cbVideo"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Choose Video Menu:</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,153 @@
#include "avm_menu_edit.h"
#include "ui_avm_menu_edit.h"
#include <AudioVideoManagement/avm_dtc.h>
namespace AudioVideoManagement
{
MenuEdit::MenuEdit(QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_ftc(nullptr)
, m_ui(new Ui::MenuEdit)
, m_availStreamsModel(nullptr)
, m_usedStreamsModel(nullptr)
, m_isAudio(false)
, m_logger(getLogger("AudioVideoManagementEdit"))
, m_requestHelper(new LtsUtils::RequestHelper(this))
{
m_ui->setupUi(this);
}
MenuEdit::~MenuEdit()
{
delete m_ui;
}
void MenuEdit::retranslate()
{
if (m_isAudio)
translate(m_ui, tr("Edit Audio Menu: %1").arg(m_name));
else
translate(m_ui, tr("Edit Video Menu: %1").arg(m_name));
}
bool MenuEdit::init(LtsSession::LtsSession *session,
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc,
const std::shared_ptr<AudioVideoManagement::Interface::Ftc> &ftc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
StreamNameMapping *streamNameMapping,
const MenuStruct &ms)
{
DEBUG_LOG(m_logger) << "MenuEdit::init()";
if (!ftc || !dtc)
{
ERROR_LOG(m_logger) << "MenuEdit::init() Invalid shared_ptr!";
return false;
}
m_ftc = ftc;
m_isAudio = ms.isAudio();
m_name = QString::fromStdString(ms.name());
m_availStreamsModel = new AvailableStreamsModel(dtc, ismDtc, session, streamNameMapping, this, m_isAudio);
m_ui->availTreeView->setModel(m_availStreamsModel);
m_usedStreamsModel = new UsedStreamsModel(dtc, ismDtc, ms, streamNameMapping, this);
m_ui->menuTreeView->setModel(m_usedStreamsModel);
m_ui->menuTreeView->expandAll();
connect(m_ui->menuTreeView, &QTreeView::clicked,
this, &MenuEdit::onStreamSelected);
connect(m_usedStreamsModel, &UsedStreamsModel::modelDataChanged,
this, &MenuEdit::slotChangesMade);
connect(m_usedStreamsModel, &UsedStreamsModel::modelDataChanged,
this, &MenuEdit::onModelDataChanged);
m_buttonBox->setButtonVisible(LtsWidgets::DialogButton::Remove);
connect(m_buttonBox->getButton(LtsWidgets::DialogButton::Remove), &QPushButton::clicked,
this, &MenuEdit::onRemove);
onStreamSelected(QModelIndex());
if (m_isAudio)
setOnlineHelpFilename(QStringLiteral("Situation-Audio_Menu"));
else
setOnlineHelpFilename(QStringLiteral("Situation-Video_Menu"));
if (ms.id() <= 0)
m_buttonBox->setButtonVisible(LtsWidgets::DialogButton::Apply, false);
return true;
}
bool MenuEdit::apply()
{
MenuStruct ms = m_usedStreamsModel->getMenuStruct();
ms.isAudio(m_isAudio);
bool ret = m_requestHelper->startSingleRequest([this, ms]()
{
return m_ftc->modifyMenuStructRequest(ms, this);
});
return ret;
}
void MenuEdit::onRemove()
{
if (0 == m_ui->menuTreeView->selectionModel()->selectedIndexes().size())
{
QModelIndex index = m_ui->menuTreeView->currentIndex();
if (!index.isValid())
return;
slotChangesMade();
DEBUG_LOG(m_logger) << "index row: " << index.row();
m_usedStreamsModel->removeRow(index.row(), index.parent());
}
else
{
while (m_ui->menuTreeView->selectionModel()->selectedIndexes().size() > 0)
{
QModelIndex index = m_ui->menuTreeView->selectionModel()->selectedIndexes().front();
if (!index.isValid())
return;
slotChangesMade();
DEBUG_LOG(m_logger) << "index row: " << index.row();
m_usedStreamsModel->removeRow(index.row(), index.parent());
}
}
}
void MenuEdit::onModelDataChanged()
{
INFO_LOG(m_logger) << "onModelDataChanged";
m_ui->menuTreeView->expandAll();
}
void MenuEdit::onStreamSelected(const QModelIndex &idx)
{
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, idx.isValid());
}
}
@@ -0,0 +1,78 @@
#pragma once
#include "avm_available_streams_model.h"
#include "avm_used_streams_model.h"
#include <LtsWidgets/lts_dialog.h>
#include <LtsSession/lts_session.h>
#include <Services/AudioVideoManagement/avm_ftc.h>
#include <AudioVideoManagement/logger.h>
#include <LtsUtilsGuiDds/request_helper.h>
namespace Ui
{
class MenuEdit;
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class StreamNameMapping;
class MenuEdit : public LtsWidgets::LtsDialog,
protected AudioVideoManagement::Interface::Ftc::ModifyMenuStructReplyHandler
{
Q_OBJECT
public:
explicit MenuEdit(QWidget *parent = nullptr);
~MenuEdit();
bool init(LtsSession::LtsSession *session,
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc,
const std::shared_ptr<AudioVideoManagement::Interface::Ftc> &ftc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
StreamNameMapping *streamNameMapping,
const MenuStruct &ms);
void onModifyMenuStructReply(const Core::FunctionReplyObject &replyObject) override { m_requestHelper->requestReply(replyObject); }
public slots:
void onRemove();
void onModelDataChanged();
void onStreamSelected(const QModelIndex &idx);
private:
void retranslate() override;
bool apply() override final;
std::shared_ptr<AudioVideoManagement::Interface::Ftc> m_ftc;
Ui::MenuEdit *m_ui;
AvailableStreamsModel *m_availStreamsModel;
UsedStreamsModel *m_usedStreamsModel;
bool m_isAudio;
QString m_name;
NGL::INGLLogger &m_logger;
LtsUtils::RequestHelper *m_requestHelper;
AudioVideoManagement::MenuStruct m_lastDdsMenuStruct;
AudioVideoManagement::MenuStruct m_modifiedMenuStruct;
};
}
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MenuEdit</class>
<widget class="QWidget" name="MenuEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>403</width>
<height>256</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Menu Edit</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="availTreeView">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragOnly</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="menuTreeView">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::CopyAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,522 @@
#include "avm_menu_edit_view.h"
#include "avm_menu_edit_gui_plugin.h"
#include "avm_menu_edit.h"
#include "avm_choose_menu.h"
#include "avm_playback_menu_dialog.h"
#include "topic_replay.h"
#include "TopicEditor/topic_editor.h"
#include "AudioSettings/audio_settings_dialog.h"
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <LtsScriptTimeline/track_entry.h>
#include <AudioVideoManagement/Shared/stream_list_result.h>
#include <LtsUtilsSettings/settings.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
#include <Services/AudioVideoManagement/ava_session.h>
#include <Services/AudioVideoManagement/ava_control.h>
#include <Services/AudioVideoManagement/ava_types.h>
#include <Services/AudioVideoManagement/avm_operator_rights.h>
#include <Services/AudioVideoManagement/avs_dialog.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <QFileDialog>
namespace AudioVideoManagement
{
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.Avm.MenuEdit"))
{
Q_ASSERT(session);
Q_ASSERT(session->partitionType() == IDL::PartitionType::Exercise || session->partitionType() == IDL::PartitionType::Replay);
}
bool InterfaceImpl::fetchInterfaces()
{
if (!m_session->getForeignDataInterface_NEW(m_avmDataIfc))
return false;
m_dtc = m_avmDataIfc->getDtc();
m_ftc = m_avmDataIfc->getFtc();
return true;
}
void InterfaceImpl::clearInterfaces()
{
m_ftc = nullptr;
m_dtc = nullptr;
m_avmDataIfc = nullptr;
}
QString InterfaceImpl::menuGroup() const
{
return tr("Audio / Video");
}
QString InterfaceImpl::playbackMenuGroup() const
{
return tr("Playbacks");
}
static inline QString s_avmPlaybackDialogIdentifierVideo() { return QStringLiteral("AvmPlaybackDialogVideo"); }
static inline QString s_avmPlaybackDialogIdentifierAudio() { return QStringLiteral("AvmPlaybackDialogAudio"); }
static inline QString s_avmVideoFullScreen() { return QStringLiteral("AvmVideoFullscreen"); }
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> actions;
LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance();
const bool enableAudioVideoManagement = ps->value(QStringLiteral("avm"),
QStringLiteral("enableAudioVideoManagement")).toBool();
const bool enableVideoImport = ps->value(QStringLiteral("avm"),
QStringLiteral("enableVideoImport")).toBool();
const bool enableAudioVideoPlaybacks = ps->value(QStringLiteral("avm"),
QStringLiteral("enableAudioVideoPlaybacks")).toBool();
if (m_session->isExercisePartition() && enableAudioVideoManagement)
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Video Menu");
plData.identifier = QLatin1String("avmMenuVideoView");
plData.menuIcon = QIcon(QLatin1String("icon:video_menu.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvMenuEdit;
plData.exercisePart = LtsWidgets::ExercisePart::ePlanning;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowMenuEditVideoView(parent);
};
actions.append(plData);
}
if (m_session->isExercisePartition() && enableAudioVideoManagement)
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Audio Menu");
plData.identifier = QLatin1String("avmMenuAudioView");
plData.menuIcon = QIcon(QLatin1String("icon:announcement_menu.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvMenuEdit;
plData.exercisePart = LtsWidgets::ExercisePart::ePlanning;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowMenuEditAudioView(parent);
};
actions.append(plData);
}
if (m_session && m_session->app() == LtsAppDescriptor::App::Situation &&
m_session->isExercisePartition() && enableAudioVideoManagement)
{
LtsWidgets::PluginData plData1;
plData1.menuGroup = menuGroup();
plData1.menuText = tr("Voice Communication Access");
plData1.identifier = QLatin1String("_OpmShowAudioSettings");
plData1.menuIcon = QIcon(QLatin1String("icon:voice_communication_access.svg"));
plData1.exercisePart = LtsWidgets::ExercisePart::ePlanning;
plData1.operatorRight = OperatorRights::AudioVideoManagement::AvVoiceCommunicationAccess;
plData1.createWidgetFunc = [this](QWidget * parent)
{
return onShowAudioSettingsDialog(parent);
};
actions.append(plData1);
}
if (!m_avmDataIfc->hasAvsSession())
{
DEBUG_LOG(m_logger) << "No avs actions for this session, as it is missing an avs session";
return actions;
}
if (enableAudioVideoManagement)
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Choose AV Menu");
plData.identifier = QLatin1String("avmChooseMenu");
plData.menuIcon = QIcon(QLatin1String("icon:choose_audio_video_menu.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvMenuShow;
plData.exercisePart = LtsWidgets::ExercisePart::eExecution;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowChooseMenu(parent);
};
actions.append(plData);
}
if (enableAudioVideoPlaybacks)
{
LtsWidgets::PluginData plData;
plData.menuGroup = playbackMenuGroup();
plData.menuText = tr("Video Playback");
plData.identifier = s_avmPlaybackDialogIdentifierVideo();
plData.menuIcon = QIcon(QLatin1String("icon:playback_video.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvPlayback;
plData.exercisePart = LtsWidgets::ExercisePart::eAfterAction;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowVideoPlayback(parent);
};
actions.append(plData);
plData.menuText = tr("Audio Playback");
plData.identifier = s_avmPlaybackDialogIdentifierAudio();
plData.menuIcon = QIcon(QLatin1String("icon:playback_audio.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvPlayback;
plData.exercisePart = LtsWidgets::ExercisePart::eAfterAction;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowAudioPlayback(parent);
};
actions.append(plData);
if (m_session->isExercisePartition())
{
plData.menuGroup = menuGroup();
plData.menuText = tr("Video Fullscreen");
plData.identifier = s_avmVideoFullScreen();
plData.menuIcon = QIcon(QLatin1String("icon:video_menu.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvPlayback;
plData.exercisePart = LtsWidgets::ExercisePart::eExecution;
plData.createWidgetFunc = [this](QWidget * parent)
{
return onShowVideoFullscreen(parent);
};
actions.append(plData);
#ifdef LTS_DEBUG
LtsWidgets::PluginData plDataD;
plDataD.menuGroup = QStringLiteral("[Dev]");
plDataD.menuText = QStringLiteral("Video Fullscreen");
plDataD.identifier = s_avmVideoFullScreen() + QStringLiteral("DEV");
plDataD.createWidgetFunc = [this](QWidget * parent)
{
return onShowVideoFullscreen(parent);
};
actions.append(plDataD);
#endif
}
}
if (m_session->isExercisePartition() && enableVideoImport)
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Video Import");
plData.identifier = QLatin1String("avmVideoImport");
plData.menuIcon = QIcon(QLatin1String("icon:import_video.svg"));
plData.operatorRight = OperatorRights::AudioVideoManagement::AvImportVideo;
plData.exercisePart = LtsWidgets::ExercisePart::ePlanning;
plData.createWidgetFunc = nullptr;
plData.directFunc = [this](const LtsWidgets::PluginData &) { showImportDialog(); };
actions.append(plData);
}
return actions;
}
TopicEditorBase::TopicEditor *InterfaceImpl::createTopicEditor(const TopicEditorBase::TopicData &data)
{
return new TopicEditor(m_session, data);
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowMenuEditVideoView(QWidget *parent)
{
std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc;
if (!m_session->getDtc<InfrastructureManagement::DataInterface>(ismDtc))
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The Infrastructure Management is not available!"), tr("Cannot open Menu Edit"));
return nullptr;
}
MenuEditView *dialog = new MenuEditView(menuGroup(), m_session, parent);
bool isAudio = false;
dialog->init(m_dtc, m_ftc, ismDtc, m_avmDataIfc->streamNameMapping(), isAudio);
return dialog;
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowMenuEditAudioView(QWidget *parent)
{
std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc;
if (!m_session->getDtc<InfrastructureManagement::DataInterface>(ismDtc))
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The Infrastructure Management is not available!"), tr("Cannot open Menu Edit"));
return nullptr;
}
MenuEditView *dialog = new MenuEditView(menuGroup(), m_session, parent);
bool isAudio = true;
dialog->init(m_dtc, m_ftc, ismDtc, m_avmDataIfc->streamNameMapping(), isAudio);
return dialog;
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowChooseMenu(QWidget *parent)
{
return new ChooseMenu(m_dtc.get(), m_avmDataIfc->menus(), parent);
}
bool InterfaceImpl::startAudioVideoPlayback(const QString &streamName, qint64 startTime_ms, bool isAudio)
{
auto dlg = createPlaybackDialog(isAudio, nullptr);
if (dlg == nullptr)
return false;
const AvaTimePoint startTime(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(startTime_ms)));
const QString &menuGroupString(playbackMenuGroup());
const QString &identifier(isAudio ? s_avmPlaybackDialogIdentifierAudio() : s_avmPlaybackDialogIdentifierVideo());
dlg->avmPlaybackMenu()->setPlaybackPreset(streamName, startTime, true);
if (m_session->widgetExists(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab))
{
return (m_session->focusTab(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab));
}
return m_session->addWidget(dlg, menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab);
}
bool InterfaceImpl::startMarkerAudioVideoPlayback(int markerId, bool isAudio)
{
auto dlg = createPlaybackDialog(isAudio, nullptr, false);
if (dlg == nullptr)
return false;
const QString menuGroupString(playbackMenuGroup());
const QString identifier(isAudio ? s_avmPlaybackDialogIdentifierAudio() : s_avmPlaybackDialogIdentifierVideo());
dlg->avmPlaybackMenu()->setMarkerPreset(markerId, true);
if (m_session->widgetExists(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab))
{
return (m_session->focusTab(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab));
}
return m_session->addWidget(dlg, menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab);
}
StreamListResult *InterfaceImpl::getAsyncStreamList(AvaStreamType streamType, AvaStreamSource streamSource)
{
if (!m_avmDataIfc->hasAvsSession())
{
ERROR_LOG(m_logger) << "No AvsSession available";
return nullptr;
}
auto *streamListResult = new StreamListResult();
m_avaSession = m_avmDataIfc->avaSession();
auto fn = [this, streamListResult, streamType, streamSource]()
{
m_avaSession->control()->getStreamList(streamType, streamSource, streamListResult, &StreamListResult::onStreamListReceived);
};
if (m_avaSession->isEstablished())
fn();
else
connect(m_avaSession, &AvaSession::sessionEstablished,
this, fn);
return streamListResult;
}
StreamClipsResult *InterfaceImpl::getAsyncStreamClips(const QString &streamName, const boost::optional<AvaTimePoint> &start, const boost::optional<AvaTimePoint> &end)
{
if (!m_avmDataIfc->hasAvsSession())
{
ERROR_LOG(m_logger) << "No AvsSession available";
return nullptr;
}
m_avaSession = m_avmDataIfc->avaSession();
auto *streamClipsResult = new StreamClipsResult();
auto fn = [this, streamClipsResult, streamName, start, end]()
{
m_avaSession->control()->queryTimeline(streamName, streamClipsResult, &StreamClipsResult::onClipsListReceived, start, end);
};
if (m_avaSession->isEstablished())
fn();
else
connect(m_avaSession, &AvaSession::sessionEstablished,
this, fn);
return streamClipsResult;
}
TopicReplay::Interface *InterfaceImpl::createInterface(TopicEditorBase::TopicType topicType)
{
Q_ASSERT(topicType == TopicEditorBase::TopicType::Audio || topicType == TopicEditorBase::TopicType::Video);
bool isAudio = topicType == TopicEditorBase::TopicType::Audio;
return new TopicReplay(m_avmDataIfc.get(), isAudio);
}
AvmPlaybackMenuDialog *InterfaceImpl::createPlaybackDialog(bool isAudio, QWidget *parent, bool setInitialSearchContentMarkers)
{
PlaybackApp *app = nullptr;
QPointer<AvmPlaybackMenuDialog> &dialogPtr = (isAudio ? m_avmPlaybackMenuDialogAudio : m_avmPlaybackMenuDialogVideo);
if (dialogPtr)
return dialogPtr;
if (isAudio)
app = m_avmDataIfc->audioPlaybackApp();
else
app = m_avmDataIfc->videoPlaybackApp();
if (!m_avmDataIfc->hasAvsSession())
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The audio/video interface is not available in this session."));
return nullptr;
}
if (app->inUse())
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The audio/video interface is already in use by another dialog."));
return nullptr;
}
dialogPtr = new AvmPlaybackMenuDialog(isAudio, parent);
if (!dialogPtr->avmPlaybackMenu()->init(m_session, m_avmDataIfc->avaSession(), PlaybackAppHandle(app),
m_avmDataIfc->streamNameMapping(), isAudio, false, {}, {}, setInitialSearchContentMarkers))
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayout(), tr("The audio/video interface could not be initialized. Please try again later."));
dialogPtr->deleteLater();
return nullptr;
}
AvmPlaybackMenu *apm = dialogPtr->avmPlaybackMenu();
connect(apm, &AvmPlaybackMenu::signalSetOverlay,
this, [dialogPtr](QWidget * widget)
{
dialogPtr->setOverlay(widget);
});
connect(apm, &AvmPlaybackMenu::signalUnsetOverlay,
this, [dialogPtr]()
{
dialogPtr->unsetOverlay();
});
return dialogPtr;
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowVideoPlayback(QWidget *parent)
{
return createPlaybackDialog(false, parent);
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowAudioPlayback(QWidget *parent)
{
return createPlaybackDialog(true, parent);
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowAudioSettingsDialog(QWidget *parent)
{
AudioSettingsDialog *dialogAudio = new AudioSettingsDialog(parent);
return LtsWidgets::initDialog(dialogAudio, m_avmDataIfc->getOpmDtc(), m_dtc.get(), m_ftc.get(), m_session);
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowVideoFullscreen(QWidget *parent)
{
Q_UNUSED(parent);
m_avmDataIfc->showOrCloseAvsApp();
return nullptr;
}
void InterfaceImpl::showImportDialog()
{
const QString &dialogId = AvsDialog::IMPORT;
if (m_importDialogVisible)
{
m_avmDataIfc->avaSession()->control()->setVideoDialogProfile(dialogId, AvaVisibility::Hidden);
m_avmDataIfc->avaSession()->control()->releaseImport(dialogId);
m_importDialogVisible = false;
return;
}
QWidget *parent = nullptr;
QString path = QFileDialog::getExistingDirectory(parent, tr("Choose directory"),
QStringLiteral("/mnt"),
QFileDialog::ShowDirsOnly | QFileDialog::ReadOnly);
if (path.size())
{
m_avmDataIfc->avaSession()->control()->setVideoDialogProfile(dialogId, AvaVisibility::Visible);
m_avmDataIfc->avaSession()->control()->chooseImport(dialogId, path, 1);
m_importDialogVisible = true;
}
else
m_avmDataIfc->avaSession()->control()->releaseImport(dialogId);
}
}
@@ -0,0 +1,103 @@
#pragma once
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/ava_session.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <Plugins/AudioVideoManagement/avm_menu_edit_gui_plugin_interface.h>
#include <memory>
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
namespace Interface
{
class Dtc;
class Ftc;
}
class AvaControl;
class DataInterface;
class AvmPlaybackMenuDialog;
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "gui:3:avm.mne")
Q_INTERFACES(GuiPluginBase::Plugin)
Q_OBJECT
public:
GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return QLatin1String("AudioVideoManagementMenuEditGuiPlugin"); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Exercise || partition == IDL::PartitionType::Replay; }
};
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface, public ForeignMenuEditGuiInterface
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
~InterfaceImpl() = default;
bool fetchInterfaces() override;
void clearInterfaces() override;
QString menuGroup() const override;
QString playbackMenuGroup() const;
QVector<LtsWidgets::PluginData> getActions() override;
TopicEditorBase::TopicEditor *createTopicEditor(const TopicEditorBase::TopicData &data) override;
bool startAudioVideoPlayback(const QString &streamName, qint64 startTime_ms, bool isAudio) override final;
bool startMarkerAudioVideoPlayback(int markerId, bool isAudio) override;
StreamListResult* getAsyncStreamList(AvaStreamType streamType = AvaStreamType::Video, AvaStreamSource streamSource = AvaStreamSource::Server) override;
StreamClipsResult* getAsyncStreamClips(const QString &streamName, const boost::optional<AvaTimePoint> &start = {}, const boost::optional<AvaTimePoint> &end = {}) override;
TopicReplay::Interface *createInterface(TopicEditorBase::TopicType topicType) override;
private:
class TopicReplayImpl;
LtsWidgets::LtsDialog *onShowMenuEditVideoView(QWidget *parent);
LtsWidgets::LtsDialog *onShowMenuEditAudioView(QWidget *parent);
LtsWidgets::LtsDialog *onShowChooseMenu(QWidget *parent);
AvmPlaybackMenuDialog *createPlaybackDialog(bool isAudio, QWidget *parent, bool setInitialSearchContentMarkers = true);
LtsWidgets::LtsDialog *onShowVideoPlayback(QWidget *parent);
LtsWidgets::LtsDialog *onShowAudioPlayback(QWidget *parent);
LtsWidgets::LtsDialog *onShowAudioSettingsDialog(QWidget *parent);
LtsWidgets::LtsDialog *onShowVideoFullscreen(QWidget *parent);
void showImportDialog();
AvaSession *m_avaSession;
std::shared_ptr<AudioVideoManagement::DataInterface> m_avmDataIfc;
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_dtc;
std::shared_ptr<AudioVideoManagement::Interface::Ftc> m_ftc;
bool m_importDialogVisible{false};
QPointer<AvmPlaybackMenuDialog> m_avmPlaybackMenuDialogAudio;
QPointer<AvmPlaybackMenuDialog> m_avmPlaybackMenuDialogVideo;
};
}
@@ -0,0 +1,246 @@
#include "avm_menu_edit_view.h"
#include "ui_avm_menu_edit_view.h"
#include "avm_menu_edit.h"
#include "avm_new_menu_struct.h"
#include <LtsSession/lts_session_manager.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <FileManagement/flm_translation.h>
#include <LtsPrinting/printing.h>
#include <QTreeView>
namespace AudioVideoManagement
{
MenuEditView::MenuEditView(const QString &menuGroup, LtsSession::LtsSession *session, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_menuGroup(menuGroup)
, m_model(nullptr)
, m_isAudio(false)
, m_dtc(nullptr)
, m_ftc(nullptr)
, m_session(session)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_ui(new Ui::MenuEditView)
, m_logger(getLogger("AudioVideoManagementDialog"))
{
m_ui->setupUi(this);
m_ui->widget->setSelectionMode(QAbstractItemView::SingleSelection);
connect(m_ui->widget, &LtsDataView::LtsDataView::signalSelectionChanged,
this, &MenuEditView::onSelectionChanged);
connect(m_ui->widget, &LtsDataView::LtsDataView::signalDoubleClicked,
this, &MenuEditView::onEditMenuStruct);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Edit, this, &MenuEditView::onEditMenuStruct);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove, this, &MenuEditView::onRemoveMenuStruct);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Add, this, &MenuEditView::onAddMenuStruct);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, false);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, false);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &MenuEditView::onPrint);
}
MenuEditView::~MenuEditView()
{
delete m_ui;
}
void MenuEditView::retranslate()
{
if (m_isAudio)
translate(m_ui, tr("Audio Menu"));
else
translate(m_ui, tr("Video Menu"));
}
bool MenuEditView::init(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc,
const std::shared_ptr<AudioVideoManagement::Interface::Ftc> &ftc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
StreamNameMapping *streamNameMapping,
bool isAudio)
{
Q_ASSERT(dtc);
Q_ASSERT(ftc);
Q_ASSERT(ismDtc);
Q_ASSERT(streamNameMapping);
m_dtc = dtc;
m_ftc = ftc;
m_ismDtc = ismDtc;
m_streamNameMapping = streamNameMapping;
m_model = new MenuViewModel(this);
m_model->initModel(m_dtc, isAudio);
m_ui->widget->setModel(m_model);
m_isAudio = isAudio;
#ifndef LTS_DEBUG
m_ui->widget->hideColumn(0);
#endif
m_ui->widget->setSortIndicator(1, Qt::AscendingOrder);
m_buttonBox->connectLibraryButton(this,
&MenuEditView::onImportButtonClicked,
&MenuEditView::onExportButtonClicked);
if (!m_session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(m_flmGui))
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Library, false);
if (m_isAudio)
setOnlineHelpFilename(QStringLiteral("Situation-Audio_Menu"));
else
setOnlineHelpFilename(QStringLiteral("Situation-Video_Menu"));
return true;
}
void MenuEditView::onSelectionChanged()
{
const QModelIndex idx = m_ui->widget->currentIndex();
int row = idx.row();
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, (row >= 0));
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, (row >= 0));
}
void MenuEditView::onAddMenuStruct()
{
NewMenuStruct *newDialog = new NewMenuStruct(this);
connect(newDialog, &NewMenuStruct::newName, this, [this](const QString & name)
{
MenuStruct ms;
ms.name(name.toStdString());
ms.isAudio(m_isAudio);
MenuEdit *dialog = new MenuEdit(this);
dialog->init(m_session, m_dtc, m_ftc, m_ismDtc, m_streamNameMapping, ms);
addDialog(dialog, m_menuGroup, QLatin1String("avmNewMenu") + name, LtsWidgets::LayoutArea::eDialogTab);
});
newDialog->show();
connect(newDialog, &NewMenuStruct::accepted, newDialog, &NewMenuStruct::deleteLater);
}
void MenuEditView::onEditMenuStruct()
{
const QModelIndex idx = m_ui->widget->currentIndex();
int row = idx.row();
if (row < 0)
return;
AudioVideoManagement::MenuStruct ms = m_model->getRowData(row);
ms.isAudio(m_isAudio);
DEBUG_LOG(m_logger) << "---onEditMenuStruct () "
<< " id: " << ms.id()
<< " name: " << ms.name();
MenuEdit *dialog = new MenuEdit(this);
dialog->init(m_session, m_dtc, m_ftc, m_ismDtc, m_streamNameMapping, ms);
addDialog(dialog, m_menuGroup, QLatin1String("avmMenuEdit") + QString::number(ms.id()), LtsWidgets::LayoutArea::eDialogTab);
}
void MenuEditView::onRemoveMenuStruct()
{
const QModelIndex idx = m_ui->widget->currentIndex();
if (!idx.isValid())
return;
const AudioVideoManagement::MenuStruct ms = m_model->getRowData(idx.row());
const Core::RemoveElementRequest req(ms.id());
m_requestHelper->startSingleRequest([req, this]()
{
return m_ftc->removeMenuStructRequest(req, this);
});
if (m_model->rowCount() == 0)
{
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, false);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, false);
}
}
void MenuEditView::onPrint()
{
LtsPrinting::printView(m_session, this, m_ui->widget->treeView());
}
void MenuEditView::tableColumnOrganiserSetting()
{
m_ui->widget->setColumnsOrganiserEnabled(identifier(), true);
}
void MenuEditView::onExportButtonClicked()
{
const QModelIndex idx = m_ui->widget->currentIndex();
int row = idx.row();
if (row < 0)
return;
QByteArray byteArray = m_model->modelToJson(row);
int fileId = m_flmGui->saveFileContent(this, FileManagement::FileType::Xml, m_session->exerciseId(), byteArray);
if (fileId > 0)
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
else
ERROR_LOG(m_logger) << "Could not save file in library ";
}
void MenuEditView::onImportButtonClicked()
{
DEBUG_LOG(m_logger) << " onImportButtonClicked() ";
QByteArray content;
int fileId = m_flmGui->loadFileContent(this, content, {FileManagement::FileType::Xml}, m_session->exerciseId(), FileManagement::toString(FileManagement::XmlType::AVMMenuEditView));
if (fileId > 0)
{
if (!m_flmGui->isXmlType(content, FileManagement::toString(FileManagement::XmlType::AVMMenuEditView)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
const QVector<AudioVideoManagement::MenuStruct> menuStructs = m_model->jsonToMenuStructs(content);
for (const AudioVideoManagement::MenuStruct &request : menuStructs)
{
m_requestHelper->startSingleRequest([this, request]()
{
return m_ftc->modifyMenuStructRequest(request, this);
});
}
}
INFO_LOG(m_logger) << "--- onImportButtonClicked() ";
}
}
}
@@ -0,0 +1,84 @@
#pragma once
#include "avm_menu_view_model.h"
#include <Services/AudioVideoManagement/avm_dtc.h>
#include <Services/AudioVideoManagement/avm_ftc.h>
#include <LtsWidgets/lts_dialog.h>
#include <LtsSession/lts_session.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
namespace Ui
{
class MenuEditView;
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace AudioVideoManagement
{
class StreamNameMapping;
class MenuEditView : public LtsWidgets::LtsDialog,
protected AudioVideoManagement::Interface::Ftc::ModifyMenuStructReplyHandler,
protected AudioVideoManagement::Interface::Ftc::RemoveMenuStructReplyHandler
{
Q_OBJECT
public:
explicit MenuEditView(const QString &menuGroup, LtsSession::LtsSession *session, QWidget *parent = nullptr);
~MenuEditView();
bool init(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc,
const std::shared_ptr<AudioVideoManagement::Interface::Ftc> &ftc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
StreamNameMapping *streamNameMapping,
bool isAudio);
private:
void retranslate() override;
void onSelectionChanged();
void onAddMenuStruct();
void onEditMenuStruct();
void onRemoveMenuStruct();
void onModifyMenuStructReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
void onRemoveMenuStructReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
void onPrint();
void tableColumnOrganiserSetting() override;
private slots:
void onExportButtonClicked();
void onImportButtonClicked();
private:
QString m_menuGroup;
MenuViewModel *m_model;
bool m_isAudio;
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_dtc;
std::shared_ptr<AudioVideoManagement::Interface::Ftc> m_ftc;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
StreamNameMapping *m_streamNameMapping{nullptr};
LtsSession::LtsSession *m_session;
LtsUtils::RequestHelper *m_requestHelper;
Ui::MenuEditView *m_ui;
LOGGER_T m_logger;
};
}
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MenuEditView</class>
<widget class="QWidget" name="MenuEditView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>455</width>
<height>341</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Menu Edit View</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="LtsDataView::LtsDataView" name="widget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>250</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,151 @@
#include "avm_menu_view_model.h"
#include <FileManagement/flm_translation.h>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
namespace AudioVideoManagement
{
MenuViewModel::MenuViewModel(QObject *parent)
: LtsWidgets::LtsAbstractTableModel<AudioVideoManagement::MenuStruct>({tr("Id"),
tr("Name")},
parent)
, m_isAudio(false)
, m_dtc(nullptr)
, m_logger(getLogger("MenuViewModel"))
{}
bool MenuViewModel::initModel(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc, bool isAudio)
{
m_isAudio = isAudio;
m_dtc = dtc;
connect(m_dtc.get(), &AudioVideoManagement::Interface::Dtc::menuStructChanged,
this, &MenuViewModel::onDataChanged);
m_dtc->menuStructs([this](const AudioVideoManagement::MenuStruct &ms) { insertRow(ms); });
return true;
}
QVariant MenuViewModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole || !indexIsValid(index))
return QVariant();
const AudioVideoManagement::MenuStruct &ms = m_vecData.at(index.row());
switch (index.column())
{
case 0:
return ms.id();
case 1:
return QString::fromStdString(ms.name());
default:
break;
}
return QVariant();
}
bool MenuViewModel::isFiltered(const MenuStruct &data, DataModification modType) const
{
Q_UNUSED(modType);
if (data.isAudio() != m_isAudio)
return true;
if (data.isDefault())
return true;
return false;
}
QByteArray MenuViewModel::modelToJson(int row) const
{
if (row < 0 || row >= m_vecData.size())
return QByteArray("{}");
QJsonObject obj;
obj[QStringLiteral("XmlType")] = FileManagement::toString(FileManagement::XmlType::AVMMenuEditView);
DEBUG_LOG(m_logger) << "row =" << ": " << row;
const AudioVideoManagement::MenuStruct &menuStruct = m_vecData.at(row);
obj[QStringLiteral("id")] = 0;
obj[QStringLiteral("isAudio")] = menuStruct.isAudio();
obj[QStringLiteral("isDefault")] = menuStruct.isDefault();
obj[QStringLiteral("name")] = QString::fromStdString(menuStruct.name());
QJsonArray arrData;
for (const AudioVideoManagement::AvMenuEntry &avMenuEntry : menuStruct.menuEntries())
{
QJsonObject innerData;
innerData[QStringLiteral("id")] = avMenuEntry.id();
innerData[QStringLiteral("isGroup")] = avMenuEntry.isGroup();
if (avMenuEntry.mountingPointId().is_set())
innerData[QStringLiteral("mountingPointId")] = avMenuEntry.mountingPointId().get();
if (avMenuEntry.name().is_set())
innerData[QStringLiteral("name")] = QString::fromStdString(avMenuEntry.name().get());
innerData[QStringLiteral("parentId")] = avMenuEntry.parentId();
innerData[QStringLiteral("rowId")] = avMenuEntry.rowId();
arrData += innerData;
}
obj[QStringLiteral("menuEntries")] = arrData;
QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
QVector<AudioVideoManagement::MenuStruct> MenuViewModel::jsonToMenuStructs(const QByteArray &content) const
{
QVector<AudioVideoManagement::MenuStruct> result;
AudioVideoManagement::MenuStruct menuStruct;
const QJsonDocument doc(QJsonDocument::fromJson(content));
const QJsonObject obj(doc.object());
menuStruct.id(0);
menuStruct.isAudio(obj[QStringLiteral("isAudio")].toBool());
menuStruct.isDefault(obj[QStringLiteral("isDefault")].toBool());
menuStruct.name(obj[QStringLiteral("name")].toString().toStdString());
const QJsonArray arrEntry(obj.value(QStringLiteral("menuEntries")).toArray());
menuStruct.menuEntries().resize(arrEntry.size());
INFO_LOG(m_logger) << "arrEntry.size()=" << arrEntry.size();
for (int j = 0; j < arrEntry.size(); ++j)
{
const QJsonObject objEntry(arrEntry.at(j).toObject());
AudioVideoManagement::AvMenuEntry avMenuEntry;
avMenuEntry.id(-10000000 + objEntry[QStringLiteral("id")].toInt());
avMenuEntry.isGroup(objEntry[QStringLiteral("isGroup")].toBool());
avMenuEntry.mountingPointId(objEntry[QStringLiteral("mountingPointId")].toInt());
if (objEntry.contains(QStringLiteral("name")))
avMenuEntry.name().set(objEntry[QStringLiteral("name")].toString().toStdString());
if (objEntry[QStringLiteral("isGroup")].toBool())
avMenuEntry.parentId(0);
else
avMenuEntry.parentId(-10000000 + objEntry[QStringLiteral("parentId")].toInt());
avMenuEntry.rowId(objEntry[QStringLiteral("rowId")].toInt());
menuStruct.menuEntries()[j] = avMenuEntry;
}
result.append(menuStruct);
return result;
}
}
@@ -0,0 +1,35 @@
#pragma once
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/logger.h>
#include <LtsWidgets/lts_abstract_table_model.h>
namespace AudioVideoManagement
{
class MenuViewModel final : public LtsWidgets::LtsAbstractTableModel<AudioVideoManagement::MenuStruct>
{
Q_OBJECT
public:
explicit MenuViewModel(QObject *parent = nullptr);
bool initModel(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc, bool isAudio);
QVariant data(const QModelIndex &, int) const override final;
QByteArray modelToJson(int row) const;
QVector<AudioVideoManagement::MenuStruct> jsonToMenuStructs(const QByteArray &content) const;
protected:
bool isFiltered(const MenuStruct &data, DataModification modType) const override final;
private:
bool m_isAudio;
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_dtc;
LOGGER_T m_logger;
};
}
@@ -0,0 +1,40 @@
#include "avm_new_menu_struct.h"
#include "ui_avm_new_menu_struct.h"
#include <LtsSession/lts_session_manager.h>
namespace AudioVideoManagement
{
NewMenuStruct::NewMenuStruct(QWidget *parent)
: LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal)
, m_ui(new Ui::NewMenuStruct)
, m_logger(getLogger("NewMenu"))
{
m_ui->setupUi(this);
m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Ok)->hide();
m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Apply)->hide();
connect(m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked, this, &LtsWidgets::LtsFramelessDialog::accept);
m_ui->ltsDialogButtonBox->connectButton(LtsWidgets::DialogButton::Add, this, &NewMenuStruct::onSetNewName);
}
NewMenuStruct::~NewMenuStruct()
{
delete m_ui;
}
void NewMenuStruct::onSetNewName()
{
if (m_ui->leName->text().isEmpty())
return;
emit newName(m_ui->leName->text());
close();
}
}
@@ -0,0 +1,36 @@
#pragma once
#include <AudioVideoManagement/logger.h>
#include <LtsWidgets/lts_frameless_dialog.h>
namespace Ui
{
class NewMenuStruct;
}
namespace AudioVideoManagement
{
class NewMenuStruct : public LtsWidgets::LtsFramelessDialog
{
Q_OBJECT
public:
explicit NewMenuStruct(QWidget *parent = nullptr);
~NewMenuStruct();
private Q_SLOTS:
void onSetNewName();
Q_SIGNALS:
void newName(const QString &);
private:
Ui::NewMenuStruct *m_ui;
LOGGER_T m_logger;
};
}
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewMenuStruct</class>
<widget class="QWidget" name="NewMenuStruct">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>596</width>
<height>118</height>
</rect>
</property>
<property name="windowTitle">
<string>Create New Menu</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>New Menu:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leName"/>
</item>
</layout>
</item>
</layout>
</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>
<item>
<widget class="LtsWidgets::LtsDialogButtonBox" name="ltsDialogButtonBox"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsDialogButtonBox</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_dialog_button_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,217 @@
#pragma once
#include "ui_avm_playback_menu.h"
#include "interval_model.h"
#include <LtsSession/lts_session.h>
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <AudioVideoManagement/logger.h>
#include <MarkerManagement/mrk_ftc.h>
#include <QString>
#include <boost/optional.hpp>
#include <memory>
namespace MarkerManagement
{
class MarkerManagementDialogModel;
class MarkerSortFilterProxyModel;
class ForeignDataInterface;
}
namespace LtsUtils
{
class RequestHelper;
}
namespace AudioVideoManagement
{
class AvaSession;
class AvaControl;
class AvsSequence;
class StreamNameMapping;
class AvmPlaybackMenu;
class QueryTimeline : public QObject
{
Q_OBJECT
private:
friend class AvmPlaybackMenu;
QueryTimeline(const AvaStream &stream, const AvaTimePoint &start, const AvaTimePoint &end, AvaControl *control);
AvaControl *m_avaCtrl { nullptr };
private slots:
signals:
void signalTimelineComplete(const AvaTimeline &timeline);
};
class AvmPlaybackMenu : public QWidget,
protected MarkerManagement::Interface::Ftc::MarkerModifyReplyHandler
{
Q_OBJECT
public:
explicit AvmPlaybackMenu(QWidget *parent = nullptr);
~AvmPlaybackMenu();
bool init(LtsSession::LtsSession *session, AvaSession *avaSession, PlaybackAppHandle &&appHandle,
StreamNameMapping* streamNameMapping, bool isAudio, bool insideTopicEditor,
const boost::optional<QString> &streamName = boost::none,
const boost::optional<AvaTimePoint> &startTime = boost::none, bool setInitialSearchContentMarkers = true);
void setPlaybackPreset(const QString &streamName, const AvaTimePoint &startTime, bool autostartPresetAfterInit);
void setMarkerPreset(int markerId, bool autostartPresetAfterInit);
QueryTimeline *queryTimeline(const AvaTimePoint &start, const AvaTimePoint &end);
void onMarkerModifyReply(const Core::FunctionReplyObject &replyObject) override final;
void retranslate();
void setStreamTime(qint64 time);
void hideStreamSelection();
qint64 getCurrentTime() const;
AvaSpeed getSpeed() const;
void setSpeed(AvaSpeed newSpeed);
int getVolume() const;
void setVolume(int newVolume);
void setStreamByName(const QString &name);
QString getInternalStreamName() const;
void setStreamName(const QString &internalStreamName);
bool getSkipPause() const;
void setSkipPause(bool bSkip);
void startPlayback(bool usePresets = false);
void setSavedPos(qint64 msecs);
int currentOffset() const { return m_currentOffset; }
void selectMarker(const MarkerManagement::Marker &marker);
void selectMarker(int markerId);
signals:
void signalSetOverlay(QWidget *widget);
void signalUnsetOverlay();
void setTopicButtonStates(bool enable);
void markerSelected(const QString &description);
void signalSetStart(qint64 start);
void signalSetEnd(qint64 end);
void signalCurrentStreamChanged(const QString &displayName);
void signalTimezoneChanged(int timezoneHoursOffset);
public slots:
void onStreamSelectionChanged(const QVector<AvaStream> &selectedStreams);
private slots:
void onSessionEstablished();
void onDisconnected();
void onStreamListReceived(const QVector<AvaStream> &streams);
void onTimelineComplete(const AvaTimeline &timeline);
void onStreamPositionReceived(const QString &dialogId, const QString &streamName, AvaTimePoint);
void onVolumeReceived(AvaVolume db);
void onCommandQueueEmpty();
void onUiTimeChanged(AvaTimePoint t);
void onIntervalActivated(const QModelIndex &index);
void onMarkerClicked(const QModelIndex &index);
void onSetMarkerClicked();
void onStartClicked();
void onPauseClicked();
void onStopClicked();
void onSkipPauseClicked();
void onAllStreams();
void onIntervalBackwardClicked();
void onIntervalForwardClicked();
void onSpeedChanged();
void onVolumeChanged(int percent);
void onCurrentIntervalChanged(bool hasPreviousInterval, bool hasNextInterval, const QModelIndex &rowIndex);
void onStreamSelected();
void onMarkerStreamSelected();
void onStreamNameMappingChanged();
void onAddStreamClip();
void onAddStreamMarker();
void onRemoveSelectedStreams();
void onTabChanged(int index);
void onPauseFromPlaybackApp();
void onResumeFromPlaybackApp();
private:
enum class ButtonStates
{
AllOff,
Started,
Paused,
Stopped
};
void processPresets();
void preselectStream(const QString &fallbackInternalStreamName);
AvaSpeed currentSpeed() const;
void setButtonStates(ButtonStates);
void enableAll(bool enabled = true);
void disableAll() { enableAll(false); }
bool trySelectedStreams();
void addStream(QComboBox *cb);
void applyAvsPos();
void applyAvsVolume();
PlaybackAppHandle m_playbackApp;
bool m_isAudio = false;
StreamNameMapping *m_streamNameMapping = nullptr;
boost::optional<QString> m_presetStreamName;
boost::optional<int> m_markerIdPreset;
boost::optional<AvaTimePoint> m_presetStartTime;
class TimelineBuilder;
std::unique_ptr<TimelineBuilder> m_timelineBuilder;
std::unique_ptr<Ui::Playback> m_ui;
class TimeControl;
std::unique_ptr<TimeControl> m_timeControl;
boost::optional<QVector<AvaStream>> m_selectedStreams;
IntervalModel m_intervalModel;
boost::optional<AvaTimePoint> m_savedPos;
std::unique_ptr<MarkerManagement::MarkerManagementDialogModel> m_markerModel;
std::unique_ptr<MarkerManagement::MarkerSortFilterProxyModel> m_markerProxy;
AvaControl *m_avaCtrl = nullptr;
LtsSession::LtsSession *m_session = nullptr;
bool m_skipEmpty = true;
ButtonStates m_currentButtonState;
std::shared_ptr<MarkerManagement::ForeignDataInterface> m_mrkDataIfc;
boost::optional<AvaTimePoint> m_lastAvsPos;
boost::optional<AvaVolume> m_lastAvsVolume;
std::unique_ptr<LtsUtils::RequestHelper> m_requestHelper;
QVector<AvaStream> m_streams;
boost::optional<AvaStream> m_currentStream;
bool m_autostartOnce { false };
int m_maxSelectedStreams = 12;
bool m_queryTimelineActive { false };
int m_currentOffset = 0;
bool m_videoScreenSelectionDone { false };
bool m_timelineIsComplete { false };
ButtonStates m_buttonStateBeforePlaybackAppPause { ButtonStates::AllOff };
LOGGER_T m_logger;
};
}
@@ -0,0 +1,697 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Playback</class>
<widget class="QWidget" name="Playback">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>810</width>
<height>791</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Playback Menu</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,5,1,0,0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="lbAvsConnectionStatus">
<property name="text">
<string>AVS Connection status:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblAvsConnectionStatus">
<property name="text">
<string notr="true">unknown</string>
</property>
</widget>
</item>
<item>
<spacer name="spcAvsConnectionStatus">
<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>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Time zone:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbTimeZone">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</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>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Audio / Video clips</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,999">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="lblCbStreams">
<property name="text">
<string>From stream:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAddStreamClip">
<property name="text">
<string>Add stream</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsComboBox" name="cbStreams">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<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>
<widget class="QPushButton" name="pbDebug">
<property name="text">
<string notr="true">[debug]</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="tavIntervals">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Markers</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="LtsDataView::LtsDataView" name="tvMarker" native="true">
<property name="showColumnCount" stdset="0">
<bool>false</bool>
</property>
<property name="toolButtonsVisible" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select stream from marker:</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsComboBox" name="cbMarkerStreams">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAddStreamMarker">
<property name="text">
<string>Add stream</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAllStreams">
<property name="text">
<string>Show all streams</string>
</property>
</widget>
</item>
<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>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QWidget" name="multiStreamWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_13">
<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>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Selected streams:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="lwStreams"/>
</item>
<item>
<widget class="QPushButton" name="pbRemoveSelectedStream">
<property name="text">
<string>Remove selected</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Current Time:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbSetMarker">
<property name="text">
<string>Create Marker</string>
</property>
</widget>
</item>
<item>
<widget class="LtsWidgets::LtsMSecDateTimeEdit" name="dtCurrentTime">
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="topicEditorEssentialsWidget" native="true">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Set time as..:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAsStart">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbAsEnd">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>End</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<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>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="lblStartSeq">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slSequenceSlider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::NoTicks</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblEndSeq">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QToolButton" name="pbIntervalBackward">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Back</string>
</property>
<property name="text">
<string>Back</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_previous_track.svg</normaloff>:/style/avm_previous_track.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pbStart">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Play</string>
</property>
<property name="text">
<string>Start</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_start.svg</normaloff>:/style/avm_start.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pbPause">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Pause</string>
</property>
<property name="text">
<string>Pause</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_pause.svg</normaloff>:/style/avm_pause.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pbStop">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Stop</string>
</property>
<property name="text">
<string>Stop</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_stop.svg</normaloff>:/style/avm_stop.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pbIntervalForward">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Forward</string>
</property>
<property name="text">
<string>Forward</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_next_track.svg</normaloff>:/style/avm_next_track.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="lbSpeed">
<property name="text">
<string>Speed [%]:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbSpeedValue">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slSpeed">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="spacerSpeedVolume">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lbVolume">
<property name="text">
<string>Volume [db]:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbVolumeValue">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slVolume">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QToolButton" name="pbSkipPause">
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="toolTip">
<string>Skip/Show Pause</string>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/avm_pint.svg</normaloff>:/style/avm_pint.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsMSecDateTimeEdit</class>
<extends>QDateTimeEdit</extends>
<header>LtsWidgets/lts_msec_date_time_edit.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tavIntervals</tabstop>
<tabstop>cbMarkerStreams</tabstop>
<tabstop>pbAddStreamMarker</tabstop>
<tabstop>pbAllStreams</tabstop>
<tabstop>lwStreams</tabstop>
<tabstop>pbRemoveSelectedStream</tabstop>
<tabstop>pbSetMarker</tabstop>
<tabstop>pbAsStart</tabstop>
<tabstop>pbAsEnd</tabstop>
<tabstop>slSequenceSlider</tabstop>
<tabstop>pbIntervalBackward</tabstop>
<tabstop>pbStart</tabstop>
<tabstop>pbPause</tabstop>
<tabstop>pbStop</tabstop>
<tabstop>pbIntervalForward</tabstop>
<tabstop>slSpeed</tabstop>
<tabstop>slVolume</tabstop>
<tabstop>pbSkipPause</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,41 @@
#include "avm_playback_menu_dialog.h"
namespace AudioVideoManagement
{
AvmPlaybackMenuDialog::AvmPlaybackMenuDialog(bool isAudio, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_isAudio(isAudio)
, m_ui(new Ui::AvmPlaybackMenuDialog)
{
m_ui->setupUi(this);
if (m_isAudio)
setOnlineHelpFilename(QStringLiteral("Situation-Playback_Audio"));
else
setOnlineHelpFilename(QStringLiteral("Situation-Playback_Video"));
}
AvmPlaybackMenuDialog::~AvmPlaybackMenuDialog()
{
delete m_ui;
}
AudioVideoManagement::AvmPlaybackMenu *AvmPlaybackMenuDialog::avmPlaybackMenu()
{
return m_ui->widget;
}
void AvmPlaybackMenuDialog::retranslate()
{
translate(m_ui, m_isAudio ? tr("Audio Playback") : tr("Video Playback"));
m_ui->widget->retranslate();
}
}
@@ -0,0 +1,36 @@
#pragma once
#include "ui_avm_playback_menu_dialog.h"
#include <LtsWidgets/lts_dialog.h>
#include <LtsSession/lts_session.h>
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <AudioVideoManagement/logger.h>
#include <boost/optional.hpp>
namespace Ui {
class AvmPlaybackMenuDialog;
}
namespace AudioVideoManagement
{
class AvmPlaybackMenuDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
AvmPlaybackMenuDialog(bool isAudio, QWidget *parent);
AudioVideoManagement::AvmPlaybackMenu *avmPlaybackMenu();
~AvmPlaybackMenuDialog();
void retranslate() override;
private:
bool m_isAudio;
Ui::AvmPlaybackMenuDialog *m_ui;
};
}
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AvmPlaybackMenuDialog</class>
<widget class="QWidget" name="AvmPlaybackMenuDialog">
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="AudioVideoManagement::AvmPlaybackMenu" name="widget" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AudioVideoManagement::AvmPlaybackMenu</class>
<extends>QWidget</extends>
<header>avm_playback_menu.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,676 @@
#include "avm_used_streams_model.h"
#include "avm_available_streams_model.h"
#include <AudioVideoManagement/stream_name_mapping.h>
#include <nglwidgets/ngltreeitem.h>
#include <QMimeData>
#include <QSet>
namespace AudioVideoManagement
{
const QString UsedStreamsModel::s_MimeTypeOI = QStringLiteral("UsedStreamsModel/OrderItem");
UsedStreamsModel::UsedStreamsModel(const std::shared_ptr<Interface::Dtc> avmDtc, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc,
const MenuStruct &ms,
StreamNameMapping *streamNameMapping,
QObject *parent)
: NGLTreeModel(parent, nullptr, {tr("Streams")})
, m_avmDtc(avmDtc)
, m_ismDtc(ismDtc)
, m_dummyId(-10000000)
, m_menuStruct(ms)
, m_streamNameMapping(streamNameMapping)
, m_logger(getLogger("UsedStreamsModel"))
{
Q_ASSERT(streamNameMapping);
initModel();
}
QVariant UsedStreamsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
NGLTreeItem *item = privateData(index);
if (!item->data().canConvert<StreamModelData>())
return QVariant();
const StreamModelData availStream = item->data().value<StreamModelData>();
switch (role)
{
case Qt::DisplayRole:
case Qt::EditRole:
{
switch (index.column())
{
case 0:
return availStream.name;
default:
return QStringLiteral("??");
}
break;
}
case Qt::DecorationRole:
{
switch (index.column())
{
case 0:
{
static const QIcon folder(QStringLiteral("icon:evaluation_folder.svg"));
static const QIcon camera(QStringLiteral("icon:camera_control.svg"));
return availStream.isGroup ? folder : camera;
}
default:
break;
}
break;
}
default:
break;
}
return NGLTreeModel::data(index, role);
}
Qt::ItemFlags UsedStreamsModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = NGLTreeModel::flags(index);
if (index.isValid())
{
defaultFlags ^= Qt::ItemIsDropEnabled;
NGLTreeItem *item = privateData(index);
if (item->data().canConvert<StreamModelData>())
{
StreamModelData menuEntry = item->data().value<StreamModelData>();
defaultFlags |= Qt::ItemIsDragEnabled;
if (menuEntry.isGroup)
{
defaultFlags |= Qt::ItemIsEditable;
defaultFlags |= Qt::ItemIsDropEnabled;
}
}
}
else
defaultFlags |= Qt::ItemIsDropEnabled;
return defaultFlags;
}
QMimeData *UsedStreamsModel::mimeData(const QModelIndexList &indexes) const
{
QString type = AvailableStreamsModel::s_MimeType;
for (const QModelIndex &mi : indexes)
{
if (mimeType(mi) == s_MimeTypeOI)
{
type = s_MimeTypeOI;
break;
}
}
return getMimeData(indexes, type);
}
bool UsedStreamsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
Q_UNUSED(column);
if (action == Qt::IgnoreAction)
return true;
if (!checkDrop(data, index(row, column), parent, action))
return false;
INFO_LOG(m_logger) << "dropMimeData: action = " << action
<< ", row = " << row
<< ", parent: valid = " << parent.isValid();
int beginRow;
QModelIndex parentR;
if (row != -1)
{
parentR = parent;
beginRow = row;
}
else if (parent.isValid())
{
parentR = parent;
beginRow = rowCount(parentR);
}
else
{
parentR = QModelIndex();
beginRow = rowCount(parentR);
}
QString mimeType = data->hasFormat(s_MimeTypeOI) ? s_MimeTypeOI : AvailableStreamsModel::s_MimeType;
bool ret = insertMimeData(data, mimeType, parentR, beginRow);
if (ret)
{
fixUpData(m_rootItem);
}
return ret;
}
bool UsedStreamsModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) const
{
if (!NGLTreeModel::canDropMimeData(data, action, row, column, parent))
return false;
if (!data->hasFormat(AvailableStreamsModel::s_MimeType) && !data->hasFormat(s_MimeTypeOI))
return false;
if ((action != Qt::MoveAction) && (action != Qt::CopyAction))
return false;
if (!checkDrop(data, parent, parent, action))
return false;
if (!parent.isValid())
return true;
NGLTreeItem *item = privateData(parent);
QVariant v = item->data();
StreamModelData ms = v.value<StreamModelData>();
if (!ms.isGroup)
return false;
QString mimeType = data->hasFormat(s_MimeTypeOI) ? s_MimeTypeOI : AvailableStreamsModel::s_MimeType;
QByteArray encodedData = data->data(mimeType);
QDataStream stream(&encodedData, QIODevice::ReadOnly);
qint64 idParent, idItem, mountId;
bool group;
QVariant itemData;
stream >> idParent >> idItem >> group >> itemData >> mountId;
ms = itemData.value<StreamModelData>();
INFO_LOG(m_logger) << "Insert:"
<< "\n ms.name=" << ms.name
<< "\n ms.isGroup=" << ms.isGroup
<< "\n ms.parentId=" << ms.parentId
<< "\n ms.mountingPointId=" << ms.mountingPointId;
if (ms.isGroup)
return false;
return true;
}
QStringList UsedStreamsModel::mimeTypes() const
{
return { AvailableStreamsModel::s_MimeType, s_MimeTypeOI };
}
QString UsedStreamsModel::mimeType(const QModelIndex &index) const
{
if (index.isValid())
{
NGLTreeItem *item = privateData(index);
QVariant v = item->data();
StreamModelData ms = v.value<StreamModelData>();
if (ms.parentId == -1)
return s_MimeTypeOI;
return AvailableStreamsModel::s_MimeType;
}
return s_MimeTypeOI;
}
static void getItems(const NGLTreeItem *item,
int rowId,
int parentId,
std::vector<AudioVideoManagement::AvMenuEntry> &contToAdd,
int &newId)
{
if (item->data().canConvert<StreamModelData>())
{
StreamModelData s = item->data().value<StreamModelData>();
AvMenuEntry me;
me.id(s.id ? s.id : newId++);
me.parentId(parentId);
me.isGroup(s.isGroup);
if (s.isGroup)
me.name().set(s.name.toStdString());
else
me.mountingPointId(s.mountingPointId);
me.rowId(rowId);
contToAdd.push_back(me);
if (s.isGroup)
{
for (int row = 0; row < item->childCount(); ++row)
getItems(item->child(row), row + 1, me.id(), contToAdd, newId);
}
}
}
MenuStruct UsedStreamsModel::getMenuStruct() const
{
MenuStruct ms;
ms.id(m_menuStruct.id());
ms.name(m_menuStruct.name());
int newId = -10000000;
for (int row = 0; row < m_rootItem->childCount(); ++row)
getItems(m_rootItem->child(row), row + 1, ms.id(), ms.menuEntries(), newId);
return ms;
}
void UsedStreamsModel::insertChildren(NGLTreeItem *parent, int parentId, const std::vector<AudioVideoManagement::AvMenuEntry> &entries)
{
QVariant data;
QMap<int, int> rowIds;
for (const AudioVideoManagement::AvMenuEntry &me : entries)
{
if (me.parentId() != parentId)
continue;
data.setValue(StreamModelData::createFromServiceItem(me, m_streamNameMapping, m_avmDtc->videoTeams()));
NGLTreeItem *treeItem = new NGLTreeItem(data);
auto hierIt = rowIds.insert(me.rowId(), me.rowId());
const int rowToInsert = std::distance(rowIds.begin(), hierIt);
parent->insertChild(rowToInsert, treeItem);
if (me.isGroup())
{
DEBUG_LOG(m_logger) << "insertChildren"
<< me.mountingPointId()
<< me.name();
insertChildren(treeItem, me.id(), entries);
}
}
}
NGLTreeItem *UsedStreamsModel::getItemToCheck(const QModelIndex &index, const QModelIndex &parentIndex) const
{
NGLTreeItem *item = privateData(index);
QVariant v = item->data();
StreamModelData ms = v.value<StreamModelData>();
NGLTreeItem *itemParent = privateData(parentIndex);
const QVariant &variantParent = itemParent->data();
NGLTreeItem *itemToCheck = itemParent;
const StreamModelData &msParent = variantParent.value<StreamModelData>();
if (!ms.isGroup && msParent.isGroup)
itemToCheck = itemParent;
if (!ms.isGroup && !msParent.isGroup)
itemToCheck = m_rootItem;
return itemToCheck;
}
bool UsedStreamsModel::checkSingleItem(NGLTreeItem *target, StreamModelData &toCheck) const
{
bool retValue = true;
if (!target)
{
ERROR_LOG(m_logger) << "Nullptr exception";
return false;
}
else
{
StreamModelData msTarget = target->data().value<StreamModelData>();
if (msTarget.id == toCheck.parentId)
DEBUG_LOG(m_logger) << "Drag&drag in same folder.";
else if (msTarget.isGroup && toCheck.isGroup)
{
DEBUG_LOG(m_logger) << "Trying to drop folder into folder";
retValue = false;
}
else
{
const QVector<NGLTreeItem *> &children = target->children();
for (const NGLTreeItem *childData : children)
{
const QVariant &variantCheck = childData->data();
const StreamModelData &msChild = variantCheck.value<StreamModelData>();
if ((msChild.name == toCheck.name) && (msChild.id != toCheck.id))
{
retValue = false;
break;
}
}
}
}
return retValue;
}
bool UsedStreamsModel::checkDrop(const QMimeData *data, const QModelIndex &index, const QModelIndex &parentIndex, Qt::DropAction action) const
{
NGLTreeItem *itemToCheck = getItemToCheck(index, parentIndex);
QString mimeType = data->hasFormat(s_MimeTypeOI) ? s_MimeTypeOI : AvailableStreamsModel::s_MimeType;
QByteArray encodedData = data->data(mimeType);
QList<StreamModelData> items;
QDataStream stream(&encodedData, QIODevice::ReadOnly);
qint64 idParent, idItem;
bool containsIcon;
QVariant itemData;
QIcon tmp;
stream >> containsIcon;
while (!stream.atEnd())
{
stream >> idParent >> idItem >> itemData;
if (containsIcon)
stream >> tmp;
items.push_back(itemData.value<StreamModelData>());
}
int countDropped = 0;
int countAgainst = 0;
const QVector<NGLTreeItem *> &children = itemToCheck->children();
for (const auto &item : children)
{
const QVariant &variantCheck = item->data();
const StreamModelData &msCheck = variantCheck.value<StreamModelData>();
if (msCheck.isGroup)
continue;
++countAgainst;
}
QList<qint64> movedFolders;
bool onlyOneFolder = false;
for (StreamModelData &item : items)
{
StreamModelData msToCheck = itemToCheck->data().value<StreamModelData>();
if (movedFolders.size() == 0)
{
movedFolders.push_back(item.parentId);
}
if ((item.isGroup) && movedFolders.contains(item.parentId))
movedFolders.push_back(item.id);
else if ((movedFolders.at(0) != item.parentId) && (movedFolders.contains(item.parentId)) && (action == Qt::MoveAction))
{
DEBUG_LOG(m_logger) << "Ignoring items in sub folder while moving.";
continue;
}
else if (!movedFolders.contains(item.parentId) && (action == Qt::MoveAction))
{
DEBUG_LOG(m_logger) << "Only allow items from same folder to be moved.";
return false;
}
if (!checkSingleItem(itemToCheck, item))
{
DEBUG_LOG(m_logger) << "Not able to add item with name: " << item.name;
return false;
}
if (item.isGroup)
{
if (!onlyOneFolder)
{
onlyOneFolder = true;
continue;
}
else
{
DEBUG_LOG(m_logger) << "More than one folder selected.";
return false;
}
}
if ((msToCheck.id == item.parentId) && (Qt::MoveAction == action))
continue;
++countDropped;
}
if (countDropped + countAgainst > 12)
{
DEBUG_LOG(m_logger) << "Trying to add to many items to folder";
return false;
}
return true;
}
QSet<int> UsedStreamsModel::fillTakenCameras(NGLTreeItem *getItem) const
{
QSet<int> ret;
const QVector<NGLTreeItem *> &children = getItem->children();
for (const auto &item : qAsConst(children))
{
if (item->childCount())
ret += fillTakenCameras(item);
else
{
const QVariant &variantCheck = item->data();
const StreamModelData &msCheck = variantCheck.value<StreamModelData>();
if (msCheck.isGroup)
continue;
ret.insert(msCheck.mountingPointId);
}
}
return ret;
}
bool UsedStreamsModel::initModel()
{
beginResetModel();
StreamModelData ms;
ms.parentId = -1;
ms.id = -2;
QVariant data;
data.setValue(ms);
setRootItem(new NGLTreeItem(data));
insertChildren(m_rootItem, m_menuStruct.id(), m_menuStruct.menuEntries());
connect(m_ismDtc.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged,
this, &UsedStreamsModel::onDataChanged);
connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::videoTeamChanged,
this, &UsedStreamsModel::onDataChangedMvs);
connect(m_streamNameMapping, &StreamNameMapping::changed,
this, &UsedStreamsModel::onStreamNameDataChanged);
endResetModel();
return true;
}
NGLTreeItem *UsedStreamsModel::createItem(const QVariant &data, const QIcon &icon) const
{
StreamModelData s = data.value<StreamModelData>();
s.id = 0;
s.parentId = 0;
QVariant newData;
newData.setValue(s);
return new NGLTreeItem(newData, icon);
}
bool UsedStreamsModel::setData(const QModelIndex &idx, const QVariant &value, int role)
{
Q_UNUSED(role);
if (idx.isValid())
{
QVariant data;
NGLTreeItem *ti = privateData(idx);
StreamModelData ms = ti->data().value<StreamModelData>();
if (idx.column() == 0)
ms.name = value.toString();
data.setValue(ms);
ti->setData(data);
emit dataChanged(index(idx.row(), 0), index(idx.row(), columnCount(QModelIndex()) - 1));
return true;
}
return false;
}
void UsedStreamsModel::onDataChanged(const InfrastructureManagement::MainStructureElement &data,
const DataModification modType)
{
beginResetModel();
INFO_LOG(m_logger) << "onDataChanged m_rootItem->childCount()" << m_rootItem->childCount()
<< " data.elements().size()=" << data.elements().size();
switch (modType)
{
case DataModification::eRemoved:
break;
case DataModification::eModified:
case DataModification::eAdded:
for (const InfrastructureManagement::StructureElement &se : data.elements())
{
INFO_LOG(m_logger) << "se.id()" << se.id()
<< " se.displayName()" << se.displayName()
<< " m_rootItem->childCount()" << m_rootItem->childCount();
const QString newName = QString::fromStdString(se.displayName());
updateStreamNames(se.id(), newName, m_rootItem);
}
}
endResetModel();
emit modelDataChanged();
}
void UsedStreamsModel::onDataChangedMvs(const VideoTeam &data, const DataModification modType)
{
beginResetModel();
INFO_LOG(m_logger) << "onDataChanged m_rootItem->childCount()" << m_rootItem->childCount();
switch (modType)
{
case DataModification::eRemoved:
break;
case DataModification::eModified:
case DataModification::eAdded:
INFO_LOG(m_logger) << "se.id()" << data.id()
<< " se.displayName()" << data.streamName()
<< " m_rootItem->childCount()" << m_rootItem->childCount();
const QString newName = QString::fromStdString(data.streamName());
updateStreamNames(-1 * data.id(), newName, m_rootItem);
}
endResetModel();
emit modelDataChanged();
}
void UsedStreamsModel::onStreamNameDataChanged(int32_t mountingPointId, const QString &internalName, const QString &displayName)
{
Q_UNUSED(internalName)
beginResetModel();
INFO_LOG(m_logger) << "onDataChanged m_rootItem->childCount()" << m_rootItem->childCount();
INFO_LOG(m_logger) << "se.id()" << mountingPointId
<< " se.displayName()" << displayName
<< " m_rootItem->childCount()" << m_rootItem->childCount();
updateStreamNames(mountingPointId, displayName, m_rootItem);
endResetModel();
emit modelDataChanged();
}
void UsedStreamsModel::updateStreamNames(const int mountingPointId, const QString &newName, NGLTreeItem *item)
{
INFO_LOG(m_logger) << "updateStreamNames mountingPointId=" << mountingPointId
<< " newName=" << newName;
int childCount = item->childCount();
for (int i = 0; i < childCount; ++i)
{
NGLTreeItem *child = item->child(i);
if (!child->data().canConvert<StreamModelData>())
ERROR_LOG(m_logger) << "Error convert item";
StreamModelData usedStream = child->data().value<StreamModelData>();
INFO_LOG(m_logger) << "i=" << i
<< " usedStream.mountingPointId=" << usedStream.mountingPointId
<< " usedStream.name=" << usedStream.name
<< " mountingPointId=" << mountingPointId
<< " newName=" << newName;
if (mountingPointId == usedStream.mountingPointId)
{
INFO_LOG(m_logger) << "Set new DisplayName id=" << usedStream.id
<< " mountingPointId=" << usedStream.mountingPointId
<< " name=" << usedStream.name
<< " new Name =" << newName;
usedStream.name = newName;
QVariant dataVar;
dataVar.setValue(usedStream);
child->setData(dataVar);
break;
}
updateStreamNames(mountingPointId, newName, item->child(i));
}
}
void UsedStreamsModel::fixUpData(NGLTreeItem *item)
{
const QVector<NGLTreeItem *> &children = item->children();
for (NGLTreeItem *child : children)
{
const StreamModelData parentStream = item->data().value<StreamModelData>();
StreamModelData childStream = child->data().value<StreamModelData>();
if (childStream.isGroup && childStream.id == 0)
childStream.id = m_dummyId++;
if (!childStream.isGroup && childStream.id == 0)
childStream.id = m_dummyId++;
childStream.parentId = parentStream.id;
QVariant data;
data.setValue(childStream);
child->setData(data);
if (child->childCount() > 0)
fixUpData(child);
}
}
}
@@ -0,0 +1,76 @@
#pragma once
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/avm.hpp>
#include <AudioVideoManagement/avm_dtc.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <nglwidgets/ngltreemodel.h>
#include "avm_available_streams_model.h"
namespace AudioVideoManagement
{
class MenuStruct;
class StreamNameMapping;
class UsedStreamsModel final : public NGLTreeModel
{
Q_OBJECT
public:
UsedStreamsModel(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> avmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc,
const MenuStruct &ms,
StreamNameMapping *streamNameMapping,
QObject *parent);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &idx, const QVariant &value,
int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) override;
bool canDropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) const override;
QStringList mimeTypes() const override;
Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; }
MenuStruct getMenuStruct() const;
bool initModel();
protected:
NGLTreeItem *createItem(const QVariant &data, const QIcon &icon) const override;
private slots:
void onDataChanged(const InfrastructureManagement::MainStructureElement &data,
const DataModification modType);
void onDataChangedMvs(const AudioVideoManagement::VideoTeam &data,
const DataModification modType);
void onStreamNameDataChanged(int32_t mountingPointId, const QString &internalName, const QString &displayName);
private:
void updateStreamNames(const int mountingPointId, const QString &newName, NGLTreeItem *item);
void fixUpData(NGLTreeItem *item);
static const QString s_MimeTypeOI;
QString mimeType(const QModelIndex &index) const;
void insertNGLTreeItemChild(NGLTreeItem *item, NGLTreeItem *parent, bool asCopy);
void insertChildren(NGLTreeItem *parent, int parentId, const std::vector<AudioVideoManagement::AvMenuEntry> &entries);
const std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_avmDtc;
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
NGLTreeItem *getItemToCheck(const QModelIndex &index, const QModelIndex &parentIndex) const;
bool checkSingleItem(NGLTreeItem * target, StreamModelData & toCheck) const;
bool checkDrop(const QMimeData *data, const QModelIndex &index, const QModelIndex &parentIndex, Qt::DropAction action) const;
QSet<int> fillTakenCameras(NGLTreeItem *getItem) const;
int m_dummyId;
MenuStruct m_menuStruct;
StreamNameMapping *m_streamNameMapping;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,174 @@
#include "interval_model.h"
#include <AudioVideoManagement/av_helper.h>
#include <QVariant>
#include <QString>
#include <QColor>
#include <QModelIndex>
#include <QDateTime>
namespace AudioVideoManagement
{
IntervalModel::IntervalModel()
: LtsAbstractTableModel({tr("Start"), tr("End"), tr("Duration [s]")}, nullptr)
{
}
QString IntervalModel::dateTimeToStringy(const QDateTime &dt) const
{
return QDateTime::fromMSecsSinceEpoch(dt.toMSecsSinceEpoch() + (m_timezoneHoursOffset * 3600 * 1000)).toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz"));
}
QString IntervalModel::msecsSinceEpochToStringy(qint64 msecs) const
{
return dateTimeToStringy(QDateTime::fromMSecsSinceEpoch(msecs));
}
void IntervalModel::setTimezoneHoursOffset(int timezoneHoursOffset)
{
m_timezoneHoursOffset = timezoneHoursOffset;
emit dataChanged({}, {}, {Qt::DisplayRole});
}
QVariant IntervalModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
const AvaTimelinePart &interval = m_vecData.at(index.row());
switch (role)
{
case Qt::DisplayRole:
{
switch (index.column())
{
case Start:
return msecsSinceEpochToStringy(toMSecSinceEpoch(interval.first));
case End:
return msecsSinceEpochToStringy(toMSecSinceEpoch(interval.second));
case Duration:
{
qint64 duration = (toMSecSinceEpoch(interval.second) - toMSecSinceEpoch(interval.first));
qint64 secs = duration / 1000;
qint64 msecs = (duration % 1000);
return QStringLiteral("%1.%2").arg(secs).arg(msecs, 3, 10, QLatin1Char('0'));
}
}
Q_ASSERT(false);
break;
}
case Qt::BackgroundRole:
{
if (m_currentInterval == (2 * index.row()))
return QVariant(QColor(Qt::green));
return QVariant();
}
default:
break;
}
return QVariant();
}
void IntervalModel::reset()
{
m_currentPos.reset();
m_currentInterval.reset();
setData({});
emit currentIntervalChanged(false, false, QModelIndex());
}
void IntervalModel::setCurrentPosition(AvaTimePoint pos, bool forceSignal)
{
if (m_vecData.isEmpty())
return;
HalfIndex newInterval = intervalForPos(pos);
bool intervalChanged = newInterval != m_currentInterval;
m_currentPos = pos;
m_currentInterval = newInterval;
if (!intervalChanged && !forceSignal)
return;
bool hasPrev = newInterval >= 0;
bool hasNext = newInterval < 2 * (m_vecData.size() - 1);
QModelIndex rowIndex;
if (newInterval % 2 == 0)
rowIndex = index(newInterval / 2, 0);
emit currentIntervalChanged(hasPrev, hasNext, rowIndex);
QModelIndex topLeft = index(0, 0);
QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1);
emit dataChanged(topLeft, bottomRight, {Qt::BackgroundRole});
}
const AvaTimelinePart &IntervalModel::previousInterval() const
{
Q_ASSERT(m_currentInterval);
Q_ASSERT(*m_currentInterval != -1);
bool withinInterval = *m_currentInterval % 2 == 0;
int intervalIndex = *m_currentInterval / 2;
if (!withinInterval)
return m_vecData.at(intervalIndex);
if (intervalIndex == 0)
return m_vecData.at(intervalIndex);
const AvaTimelinePart &interval = m_vecData.at(intervalIndex);
if (*m_currentPos > interval.first + std::chrono::seconds(2))
return interval;
else
return m_vecData.at(intervalIndex - 1);
}
const AvaTimelinePart &IntervalModel::nextInterval() const
{
Q_ASSERT(m_currentInterval);
int intervalIndex = (*m_currentInterval + 2) / 2;
return m_vecData.at(intervalIndex);
}
IntervalModel::HalfIndex IntervalModel::intervalForPos(AvaTimePoint pos) const
{
Q_ASSERT(!m_vecData.isEmpty());
for (int i = 0; i < m_vecData.size(); ++i)
{
const AvaTimelinePart &interval = m_vecData.at(i);
if (pos < interval.first)
{
return 2 * i - 1;
}
if (pos >= interval.first && pos <= interval.second)
{
return 2 * i;
}
}
return 2 * m_vecData.size() - 1;
}
}
@@ -0,0 +1,66 @@
#pragma once
#include <AudioVideoManagement/ava_types.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <boost/optional.hpp>
namespace AudioVideoManagement
{
class IntervalModel : public LtsWidgets::LtsAbstractTableModel<AvaTimelinePart>
{
Q_OBJECT
public:
enum Columns
{
Start = 0,
End,
Duration,
};
IntervalModel();
QVariant data(const QModelIndex &index, int role) const override;
void reset();
bool isEmpty() const { return m_vecData.isEmpty(); }
void setCurrentPosition(AvaTimePoint pos, bool forceSignal = false);
boost::optional<AvaTimePoint> currentPosition() const { return m_currentPos; }
const AvaTimelinePart &previousInterval() const;
const AvaTimelinePart &nextInterval() const;
signals:
void currentIntervalChanged(bool hasPreviousInterval, bool hasNextInterval, const QModelIndex &rowIndex);
public slots:
void setTimezoneHoursOffset(int timezoneHoursOffset);
private:
QString dateTimeToStringy(const QDateTime &dt) const;
QString msecsSinceEpochToStringy(qint64 msecs) const;
using HalfIndex = int;
HalfIndex intervalForPos(AvaTimePoint pos) const;
boost::optional<AvaTimePoint> m_currentPos;
boost::optional<HalfIndex> m_currentInterval;
int m_timezoneHoursOffset = 0;
};
}
@@ -0,0 +1,465 @@
#include "topic_replay.h"
#include <AudioVideoManagement/ava_types.h>
#include <AudioVideoManagement/avm_data_plugin_interface.h>
#include <AudioVideoManagement/ava_control.h>
#include <AudioVideoManagement/ava_session.h>
#include <AudioVideoManagement/vd_control.h>
#include <AudioVideoManagement/vd_session.h>
#include <TopicEditorBase/topic_editor_base.h>
#include <LtsUtilsCore/json_object.h>
#include <nglsettings/nglsettings_settings.h>
#include <QWidget>
#include <QFileInfo>
#include <QJsonDocument>
#include <QDir>
#include <QtGlobal>
#include <QRect>
#include <QDebug>
#include <vector>
#include <chrono>
namespace AudioVideoManagement
{
class TopicReplay::ExportScript : public QObject
{
Q_OBJECT
public:
ExportScript(const QString &topLevelExportDir, TopicEditorBase::ExportNotifier *exportStep);
public slots:
void avScriptFinished(const QString &exportDir, AvaStreamType streamType, bool success);
private:
QString audioVideoPlaylistErstellen(const QString &dir, AvaStreamType streamType);
bool writeFile(const QByteArray &data, const QString &fileName);
QString m_topLevelExportDir;
TopicEditorBase::ExportNotifier *m_exportStep;
};
TopicReplay::TopicReplay(DataInterface *dataIfc, bool isAudio)
: m_isAudio(isAudio)
, m_dataIfc(dataIfc)
, m_logger(getLogger("TopicReplay"))
{
DEBUG_LOG(m_logger) << "TopicReplay::TopicReplay()";
Q_ASSERT(dataIfc);
}
TopicReplay::~TopicReplay()
{
DEBUG_LOG(m_logger) << "TopicReplay::~TopicReplay()";
if (m_isPlaying)
playbackStop();
}
QWidget *TopicReplay::createWidget()
{
DEBUG_LOG(m_logger) << "TopicReplay::createWidget()";
Q_ASSERT(!m_screenPlacementHelper);
if (m_isAudio)
return nullptr;
m_screenPlacementHelper.reset(new ScreenPlacementHelper());
connect(m_screenPlacementHelper.data(), &ScreenPlacementHelper::widgetPlaced, this, &TopicReplay::onWidgetPlaced);
return m_screenPlacementHelper.data();
}
void TopicReplay::slotSetScriptResponse(const AudioVideoManagement::AvaStreamInfo &streamInfo, AudioVideoManagement::AvaStreamState streamState)
{
if (streamState == AudioVideoManagement::AvaStreamState::NotPossible)
ERROR_LOG(m_logger) << "Script could not be set";
else
ERROR_LOG(m_logger) << "Script set!";
ERROR_LOG(m_logger) << "streamInfo: start" << streamInfo.duration.count();
ERROR_LOG(m_logger) << "streamState: " << static_cast<int>(streamState);
}
bool TopicReplay::playbackInit(const QJsonObject &attributes)
{
DEBUG_LOG(m_logger) << "TopicReplay::playbackInit()";
DEBUG_LOG(m_logger) << QJsonDocument(attributes).toJson();
if (m_screenPlacementHelper && !m_isAudio)
m_screenPlacementHelper->show();
m_playbackAttributes = attributes;
AvaControl *ctrl = getAvaControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "no ava control!";
return false;
}
AvaTopicData topicData(m_playbackAttributes);
if (!topicData.isValid)
{
ERROR_LOG(m_logger) << "topicData is invalid";
return false;
}
m_scriptName = topicData.scriptName();
Q_ASSERT(topicData.isAudio() == m_isAudio);
PlaybackApp *app = m_isAudio ? m_dataIfc->audioPresentationApp() : m_dataIfc->videoPresentationApp();
if (app == nullptr || app->inUse())
{
ERROR_LOG(m_logger) << "app invalid or in use!";
return false;
}
if (!m_dialogGeometry)
{
if (m_isAudio)
m_appHandle = PlaybackAppHandle(app);
else
m_appHandle = PlaybackAppHandle(app, QRect(0, 0, 0, 0));
}
else
m_appHandle = PlaybackAppHandle(app, *m_dialogGeometry);
if (!m_appHandle)
{
ERROR_LOG(m_logger) << "appHandle: missing app after creating appHandle!";
return false;
}
NGL::Settings s({});
bool isLocal = s.isDatabaseConnectionLocal();
DEBUG_LOG(m_logger) << "isLocal? " << isLocal;
AvaScriptSource source;
if (isLocal)
{
if (m_isAudio)
source = AvaScriptSource::LocalAudio;
else
source = AvaScriptSource::LocalVideo;
}
else
{
if (m_isAudio)
source = AvaScriptSource::ServerAudio;
else
source = AvaScriptSource::ServerVideo;
}
DEBUG_LOG(m_logger) << "source: " << static_cast<int>(source);
ctrl->setScript(m_appHandle.dialogId(), m_scriptName,
source, topicData.streamList(),
LtsUtils::createConnection(ctrl, &AudioVideoManagement::AvaControl::setScriptResponse,
this, &TopicReplay::slotSetScriptResponse));
return true;
}
bool TopicReplay::playbackStart(int secondsOffset)
{
DEBUG_LOG(m_logger) << "TopicReplay::playbackStart()";
if (m_screenPlacementHelper && !m_isAudio)
m_screenPlacementHelper->show();
AvaControl *ctrl = getAvaControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "ctrl == nullptr";
return false;
}
AvaTimePoint p{AvaDuration{std::chrono::seconds(secondsOffset)}};
if (!m_appHandle)
{
ERROR_LOG(m_logger) << "appHandle: missing app!";
return false;
}
ctrl->setAbsPositionScript(m_appHandle.dialogId(), m_scriptName, p);
ctrl->startScript(m_appHandle.dialogId(), m_scriptName);
m_isPlaying = true;
DEBUG_LOG(m_logger) << "m_isPlaying = true";
return true;
}
bool TopicReplay::playbackPause()
{
DEBUG_LOG(m_logger) << "TopicReplay::playbackPause()";
if (m_screenPlacementHelper)
m_screenPlacementHelper->hide();
if (!m_isPlaying)
return true;
AvaControl *ctrl = getAvaControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "no ctrl!";
return false;
}
if (!m_appHandle)
{
ERROR_LOG(m_logger) << "appHandle: missing app!";
return false;
}
ctrl->pauseScript(m_appHandle.dialogId(), m_scriptName);
return true;
}
bool TopicReplay::playbackStop()
{
DEBUG_LOG(m_logger) << "TopicReplay::playbackStop()";
if (m_screenPlacementHelper)
m_screenPlacementHelper->hide();
if (!m_isPlaying)
return true;
AvaControl *ctrl = getAvaControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "no ctrl!";
return false;
}
if (!m_appHandle)
{
ERROR_LOG(m_logger) << "appHandle: missing app!";
return false;
}
ctrl->stopScript(m_appHandle.dialogId(), m_scriptName);
ctrl->releaseScript(m_appHandle.dialogId(), m_scriptName);
m_isPlaying = false;
return true;
}
bool TopicReplay::exportTopic(const QJsonObject &attributes,
const QString &folderPath,
TopicEditorBase::ExportNotifier *exportNotifier)
{
Q_ASSERT(!m_exportScript);
VdControl *ctrl = getVdControl();
if (ctrl == nullptr)
{
ERROR_LOG(m_logger) << "ctrl missing!";
exportNotifier->setExportFailed();
return false;
}
m_exportScript.reset(new ExportScript(folderPath, exportNotifier));
AvaTopicData topicData(attributes);
m_scriptName = topicData.scriptName();
if (!topicData.isValid)
{
ERROR_LOG(m_logger) << "topicData is invalid";
exportNotifier->setExportFailed();
return false;
}
QFileInfo fileInfo(QDir(folderPath).filePath(topicData.scriptName()));
Q_ASSERT(topicData.isAudio() == m_isAudio);
ctrl->vdSetScript(m_scriptName,
m_isAudio ? AvaStreamType::Audio : AvaStreamType::Video,
0, topicData.streamList(), fileInfo.baseName(), fileInfo.absolutePath(),
this, &TopicReplay::avScriptFinished);
return true;
}
void TopicReplay::onWidgetPlaced(const QRect &geometry)
{
WARNING_LOG(m_logger) << "TopicReplay::onWidgetPlaced() - geo: " << geometry.x() << "/" << geometry.y()
<< " m_isAudio=" << m_isAudio;
m_dialogGeometry = geometry;
if (m_isAudio)
return;
PlaybackApp *app = m_isAudio ? m_dataIfc->audioPresentationApp() : m_dataIfc->videoPresentationApp();
if (app == nullptr)
{
ERROR_LOG(m_logger) << "app missing!";
return;
}
if (!app->inUse())
{
ERROR_LOG(m_logger) << "app not in use - waiting for playbackInit to aquire it";
return;
}
DEBUG_LOG(m_logger) << "setGeometry";
app->setGeometry(*m_dialogGeometry);
}
AvaControl *TopicReplay::getAvaControl()
{
if (!m_dataIfc->hasAvsSession())
return nullptr;
AvaSession *avaSession = m_dataIfc->avaSession();
if (!avaSession->isEstablished())
return nullptr;
return avaSession->control();
}
VdControl *TopicReplay::getVdControl()
{
if (!m_dataIfc->hasAvsSession())
return nullptr;
VdSession *vdSession = m_dataIfc->vdSession();
if (!vdSession->isEstablished())
return nullptr;
return vdSession->control();
}
void TopicReplay::avScriptFinished(const QString &exportDir, AvaStreamType streamType, bool success)
{
VdControl *ctrl = getVdControl();
ctrl->vdReleaseScript(m_scriptName);
m_exportScript->avScriptFinished(exportDir, streamType, success);
}
TopicReplay::ExportScript::ExportScript(const QString &topLevelExportDir, TopicEditorBase::ExportNotifier *exportStep)
: m_topLevelExportDir(topLevelExportDir)
, m_exportStep(exportStep)
{
connect(exportStep, &QObject::destroyed, this, [this]()
{
if (static_cast<TopicEditorBase::ExportNotifier *>(sender()) != m_exportStep)
return;
m_exportStep = nullptr;
});
Q_ASSERT(exportStep);
}
void TopicReplay::ExportScript::avScriptFinished(const QString &exportDir, AvaStreamType streamType, bool success)
{
QString absPathToPlaylist = audioVideoPlaylistErstellen(exportDir, streamType);
QDir toplevelExportDir(m_topLevelExportDir);
QString relativePathToPlaylist = toplevelExportDir.relativeFilePath(absPathToPlaylist);
if (m_exportStep == nullptr)
return;
if (success)
m_exportStep->setExportSuccessful(relativePathToPlaylist);
else
m_exportStep->setExportFailed();
}
QString TopicReplay::ExportScript::audioVideoPlaylistErstellen(const QString &dir, AvaStreamType streamType)
{
QDir avDir(dir);
const QFileInfoList dateienList = avDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
QByteArray dataForFile;
QString data;
dataForFile.append("[playlist]\n");
int numberOfEntries = 0;
int i = 1;
const QString tmpl1(QStringLiteral("File %1=%2\n"));
const QString tmpl2(QStringLiteral("# File %1=%2\n"));
for (const QFileInfo &fileInfo : dateienList)
{
if (streamType == AvaStreamType::Video && fileInfo.suffix() == QLatin1String(".wav"))
{
data += tmpl2.arg(QString::number(i), fileInfo.fileName());
i--;
}
else
{
data += tmpl1.arg(QString::number(i), fileInfo.fileName());
numberOfEntries++;
}
i++;
}
QString numberOfEntriesString = QStringLiteral("NumberOfEntries=") + QString::number(numberOfEntries) + QStringLiteral("\n");
dataForFile.append(numberOfEntriesString.toUtf8());
dataForFile.append(data.toUtf8());
QString fileName;
switch (streamType)
{
case AvaStreamType::Audio:
fileName = avDir.filePath(QStringLiteral("audio.pls"));
break;
case AvaStreamType::Video:
fileName = avDir.filePath(QStringLiteral("video.pls"));
break;
}
writeFile(dataForFile, fileName);
return fileName;
}
bool TopicReplay::ExportScript::writeFile(const QByteArray &data, const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
return false;
if (file.write(data) == -1)
{
file.close();
return false;
}
file.close();
return true;
}
}
#include "topic_replay.moc"
@@ -0,0 +1,120 @@
#pragma once
#include <AudioVideoManagement/logger.h>
#include <AudioVideoManagement/Shared/playback_app.h>
#include <PluginInterfaces/topic_replay_interface.h>
#include <QString>
#include <QRect>
#include <QVector>
#include <QWidget>
#include <boost/optional.hpp>
#include <memory>
#include <AudioVideoManagement/ava_control.h>
#include <AudioVideoManagement/ava_session.h>
namespace AudioVideoManagement
{
enum class AvaStreamType;
class ScreenPlacementHelper : public QWidget
{
Q_OBJECT
public:
ScreenPlacementHelper(QWidget *parent = nullptr)
: QWidget(parent)
{
setWindowTitle(QStringLiteral("Topic Replay Screen Placement Helper"));
setWindowFlags(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
activateWindow();
show();
}
protected:
void moveEvent(QMoveEvent *) override
{
emitNewGeometry();
}
void resizeEvent(QResizeEvent *) override
{
emitNewGeometry();
}
void emitNewGeometry()
{
emit widgetPlaced(geometry());
}
signals:
void widgetPlaced(const QRect &geometry);
};
class DataInterface;
class AvaControl;
class VdControl;
struct AvaScriptEntry;
class TopicReplay final : public QObject, public ::TopicReplay::Interface
{
Q_OBJECT
public:
TopicReplay(DataInterface *dataIfc, bool isAudio);
~TopicReplay();
QWidget *createWidget() override;
bool playbackInit(const QJsonObject &attributes) override;
bool playbackStart(int secondsOffset) override;
bool playbackPause() override;
bool playbackStop() override;
bool exportTopic(const QJsonObject &attributes,
const QString &folderPath,
TopicEditorBase::ExportNotifier *pExportNotifier) override;
private slots:
void onWidgetPlaced(const QRect &geometry);
void avScriptFinished(const QString &exportDir, AvaStreamType streamType, bool success);
void slotSetScriptResponse(const AvaStreamInfo &, AvaStreamState);
private:
AvaControl* getAvaControl();
VdControl* getVdControl();
class ExportScript;
const bool m_isAudio;
PlaybackAppHandle m_appHandle;
DataInterface *m_dataIfc;
QString m_scriptName;
bool m_isPlaying{false};
QScopedPointer<ScreenPlacementHelper> m_screenPlacementHelper;
boost::optional<QRect> m_dialogGeometry;
QScopedPointer<ExportScript> m_exportScript;
LOGGER_T m_logger;
QJsonObject m_playbackAttributes;
};
}