Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
Lts/Plugins/AudioVideoManagement/Gui/AVMenuEdit/AudioSettings/audio_settings_avail_streams_model.cpp
Executable
+297
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* OperatorManangement - Show all available streams
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-11-06
|
||||
*/
|
||||
|
||||
#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::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);
|
||||
}
|
||||
/*
|
||||
AvailStreamsModel::~AvailStreamsModel()
|
||||
{
|
||||
// do not delete, is deleted through root item
|
||||
//delete m_cnrRoot;
|
||||
//delete 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 a config of a different Exercise was changed the config might have been global before.
|
||||
// Thus we might need to remove this config.
|
||||
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::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;
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* OperatorManangement - Show all available streams
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-11-06
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
// CopyAction - data is filtered with proxy model
|
||||
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 /* is cnr */, 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:
|
||||
/** Don't use directly; will we done via constructor. */
|
||||
using QSortFilterProxyModel::setSourceModel;
|
||||
|
||||
AvailStreamsModel *m_sourceModel;
|
||||
std::set<StreamData> m_allocatedStreams;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
+440
@@ -0,0 +1,440 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* OperatorManagement - Operator Edit Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-02-29
|
||||
*/
|
||||
|
||||
#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;
|
||||
////////////
|
||||
// Create Model
|
||||
|
||||
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); // Drag and Drop from allocated -> available
|
||||
connect(m_modelAllocated, &AllocatedStreamsModel::droppedStreamNames, this, &AudioSettingsDialog::onDroppedInAllocated); // Drag and Drop from available -> allocated
|
||||
|
||||
connect(m_modelAvailable, &AvailStreamsModel::updateEntry, m_modelAllocated, &AllocatedStreamsModel::updateEntry);
|
||||
connect(m_modelAvailable, &AvailStreamsModel::removeEntry, this, &AudioSettingsDialog::onEntryRemoved);
|
||||
|
||||
// Glue model and view together
|
||||
m_ui->treeViewAvailable->setModel(m_filterAvailable);
|
||||
m_ui->treeViewAvailable->expandAll();
|
||||
m_ui->treeViewAllocated->setModel(m_modelAllocated);
|
||||
|
||||
// Operator Management to get Name from operator
|
||||
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);
|
||||
// Populate our Model
|
||||
|
||||
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->treeViewAllocated->setSortingEnabled(true);
|
||||
//m_ui->treeViewAllocated->sortByColumn(0,Qt::AscendingOrder );
|
||||
|
||||
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); //param is unused
|
||||
setUnchangeable(false);
|
||||
return true;
|
||||
} // end of init
|
||||
|
||||
void AudioSettingsDialog::retranslate()
|
||||
{
|
||||
translate(m_ui, tr("Voice Communication Access"));
|
||||
} // end of retranslate
|
||||
|
||||
void AudioSettingsDialog::onOperatorChanged(const OperatorManagement::User &data, DataModification modType)
|
||||
{
|
||||
// Table: opm.opm_operator
|
||||
DEBUG_LOG(m_logger) << "************** onOperatorChanged ";
|
||||
switch (modType)
|
||||
{
|
||||
case DataModification::eRemoved:
|
||||
{
|
||||
Q_ASSERT(m_operatorMap.contains(data.id()));
|
||||
|
||||
// Check, if Operator is currently open, if yes, reset any changes
|
||||
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;
|
||||
}
|
||||
}
|
||||
} // end of onOperatorChanged
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
} // end of onCnrTvcProfileChanged
|
||||
|
||||
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;
|
||||
|
||||
// insert entry in Allocated
|
||||
newStreamData.reserve(rc.data().size());
|
||||
for (const CnrTvcProfileData &profileEntry : rc.data())
|
||||
newStreamData += toStreamData(profileEntry);
|
||||
|
||||
break;
|
||||
}
|
||||
m_modelAllocated->setNewStreamData(newStreamData);
|
||||
m_filterAvailable->setAllocatedStreams(newStreamData);
|
||||
} // end of fillOperatorList
|
||||
|
||||
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();
|
||||
} // end of onUpButtonClicked
|
||||
|
||||
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();
|
||||
} // end of onDownButtonClicked
|
||||
|
||||
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);
|
||||
}
|
||||
} // end of onSelectionAllocationChanged
|
||||
|
||||
|
||||
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;
|
||||
} // end of applyRemove
|
||||
|
||||
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;
|
||||
} // end of applyModify
|
||||
|
||||
void AudioSettingsDialog::onModifyCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject)
|
||||
{
|
||||
__unchange__();
|
||||
m_requestHelper->requestReply(replyObject);
|
||||
} // end of onCreateRadioCircuitsReply
|
||||
//-----------------------------------------------------------------------------
|
||||
void AudioSettingsDialog::onRemoveCnrTvcProfileReply(const Core::FunctionReplyObject &replyObject)
|
||||
{
|
||||
__unchange__();
|
||||
m_requestHelper->requestReply(replyObject);
|
||||
} // end of onRemoveRadioCircuitsReply
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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:
|
||||
{
|
||||
// cfroebel 2019-06-26:
|
||||
// Setting back the operator would cause this slot to be called again.
|
||||
// Thus we avoid this by blocking signals.
|
||||
{
|
||||
QSignalBlocker blockSignals(m_ui->operatorComboBox);
|
||||
m_ui->operatorComboBox->setCurrentIndex(m_ui->operatorComboBox->findData(m_operatorId)); // reset position
|
||||
}
|
||||
return;
|
||||
}
|
||||
case LtsWidgets::Button::Discard:
|
||||
__unchange__(); // can't call resetChanges() by myself :(
|
||||
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}); // in case the stream was allocated forget about it
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* OperatorManangement - Operator Edit Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-02-29
|
||||
*/
|
||||
|
||||
#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}; //!< currently displayed operator
|
||||
QMap<int, QString> m_operatorMap;
|
||||
QMap<int, AudioVideoManagement::CnrTvcProfile> m_cnrTvcProfileMap; // all dds-Configs key = DDS-ID
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
+165
@@ -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>
|
||||
Executable
+186
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* OperatorManangement - Show all available streams
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-11-06
|
||||
*/
|
||||
|
||||
#include "audio_settings_used_streams_model.h"
|
||||
#include <nglwidgets/ngltreeitem.h>
|
||||
#include <QMimeData>
|
||||
#include <QFont>
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
//
|
||||
// UsedStreamsModel
|
||||
//
|
||||
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();
|
||||
}
|
||||
|
||||
// Wird ueber connect im SettingsDialog vom AvailableModel::updateEntry aufgerufen
|
||||
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();
|
||||
}
|
||||
|
||||
// Wird ueber connect im SettingsDialog vom AvailableModel::removeEntry aufgerufen
|
||||
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();
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* OperatorManangement - Show all available streams
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-11-06
|
||||
*/
|
||||
|
||||
#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 /* is cnr */, int> > m_changedStreams;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* OperatorManangement - Show all available streams
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-11-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AudioVideoManagement/avm.hpp>
|
||||
#include <QString>
|
||||
#include <QObject>
|
||||
#include <boost/operators.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
/**
|
||||
* Helper struct used by AvailStreamsModel, AllocatedStreamsModel.
|
||||
*
|
||||
* Comparision operators only consider bIsCnr and topicId, i.e. name is irrelevant.
|
||||
*/
|
||||
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}; }
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
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)
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - AVS Sequence Model
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2017-08-21
|
||||
*/
|
||||
|
||||
#include "avm_sequence_model.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QColor>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
AvmSequenceModel::AvmSequenceModel(QObject *parent)
|
||||
: LtsWidgets::LtsAbstractTableModel<AvsSequence>({QLatin1String("Order"),
|
||||
tr("Start"),
|
||||
tr("End"),
|
||||
tr("Duration [s]"),
|
||||
tr("Stream"),
|
||||
tr("Speed"),
|
||||
tr("Volume"),
|
||||
tr("Description")},
|
||||
parent)
|
||||
, m_logger(getLogger("AvmSequenceModel"))
|
||||
{}
|
||||
|
||||
void AvmSequenceModel::setTimezoneHoursOffset(int timezoneHoursOffset)
|
||||
{
|
||||
m_timezoneHoursOffset = timezoneHoursOffset;
|
||||
emit dataChanged(index(0, Start), index(rowCount()-1, End), {Qt::DisplayRole});
|
||||
}
|
||||
|
||||
void AvmSequenceModel::moveUp(const QModelIndex &idx)
|
||||
{
|
||||
// should better be done within moveRows
|
||||
int row = idx.row();
|
||||
auto it = std::find_if(m_highlightedRows.begin(), m_highlightedRows.end(), [row](int currentRow)
|
||||
{
|
||||
return (row == currentRow);
|
||||
});
|
||||
|
||||
if (it != m_highlightedRows.end())
|
||||
(*it)--;
|
||||
|
||||
moveRows(idx.parent(), idx.row(), 1, idx.parent(), idx.row() - 1);
|
||||
}
|
||||
|
||||
void AvmSequenceModel::moveDown(const QModelIndex &idx)
|
||||
{
|
||||
// should better be done within moveRows
|
||||
int row = idx.row();
|
||||
auto it = std::find_if(m_highlightedRows.begin(), m_highlightedRows.end(), [row](int currentRow)
|
||||
{
|
||||
return (row == currentRow);
|
||||
});
|
||||
|
||||
if (it != m_highlightedRows.end())
|
||||
(*it)++;
|
||||
moveRows(idx.parent(), idx.row(), 1, idx.parent(), idx.row() + 1);
|
||||
}
|
||||
|
||||
bool AvmSequenceModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
|
||||
{
|
||||
Q_UNUSED(count)
|
||||
if(sourceRow > m_vecData.size() -1 || sourceRow < 0 ||
|
||||
destinationChild > m_vecData.size() -1 || destinationChild < 0)
|
||||
return false;
|
||||
|
||||
int moveTo = destinationChild;
|
||||
if (destinationChild > sourceRow)
|
||||
moveTo++;
|
||||
|
||||
beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent, moveTo);
|
||||
m_vecData.move(sourceRow, destinationChild);
|
||||
endMoveRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline std::chrono::milliseconds fromJsonValue(const QJsonValue &val)
|
||||
{
|
||||
qint64 d = static_cast<qint64>(val.toDouble());
|
||||
return std::chrono::milliseconds(d);
|
||||
}
|
||||
|
||||
bool AvmSequenceModel::initModel(const QJsonArray &data)
|
||||
{
|
||||
m_highlightedRows.clear();
|
||||
clear();
|
||||
|
||||
AvsSequence seq;
|
||||
for (const auto &ele : data)
|
||||
{
|
||||
const QJsonObject &seqObj = ele.toObject();
|
||||
if (seqObj.isEmpty())
|
||||
continue;
|
||||
seq.internalStreamName = seqObj.value(QStringLiteral("stream")).toString();
|
||||
seq.speed = seqObj.value(QStringLiteral("speed")).toInt();
|
||||
seq.volume = seqObj.value(QStringLiteral("volume")).toInt();
|
||||
seq.start = AvaTimePoint(std::chrono::duration_cast<AvaDuration>(fromJsonValue(seqObj.value(QStringLiteral("start")))));
|
||||
seq.duration = std::chrono::duration_cast<AvaDuration>(fromJsonValue(seqObj.value(QStringLiteral("duration"))));
|
||||
seq.pint = seqObj.value(QStringLiteral("pint")).toBool();
|
||||
seq.description = seqObj.value(QStringLiteral("description")).toString();
|
||||
insertRow(seq);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonArray AvmSequenceModel::getSequencesAsJsonArray() const
|
||||
{
|
||||
QJsonArray ret;
|
||||
|
||||
if (m_vecData.size() <= 0)
|
||||
return ret;
|
||||
|
||||
for (int i = 0; i < m_vecData.size(); ++i)
|
||||
{
|
||||
const AvsSequence &seq = m_vecData.at(i);
|
||||
QJsonObject seqJson;
|
||||
seqJson.insert(QLatin1String("pos"), i);
|
||||
seqJson.insert(QLatin1String("description"), seq.description);
|
||||
|
||||
seqJson.insert(QLatin1String("start"), static_cast<qint64>(std::chrono::milliseconds(seq.start.time_since_epoch()).count()));
|
||||
seqJson.insert(QLatin1String("duration"), static_cast<qint64>(std::chrono::milliseconds(seq.duration).count()));
|
||||
seqJson.insert(QLatin1String("speed"), static_cast<int>(seq.speed));
|
||||
seqJson.insert(QLatin1String("stream"), seq.internalStreamName);
|
||||
seqJson.insert(QLatin1String("volume"), static_cast<int>(seq.volume));
|
||||
seqJson.insert(QLatin1String("pint"), seq.pint);
|
||||
ret.append(seqJson);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QString AvmSequenceModel::dateTimeToStringx(const QDateTime &dt) const
|
||||
{
|
||||
return QDateTime::fromMSecsSinceEpoch(dt.toMSecsSinceEpoch() + (m_timezoneHoursOffset * 3600 * 1000)).toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz"));
|
||||
}
|
||||
|
||||
|
||||
QString AvmSequenceModel::msecsSinceEpochToStringx(qint64 msecs) const
|
||||
{
|
||||
return dateTimeToStringx(QDateTime::fromMSecsSinceEpoch(msecs));
|
||||
}
|
||||
|
||||
|
||||
QVariant AvmSequenceModel::data(const QModelIndex &idx, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole && role != Qt::BackgroundRole)
|
||||
return QVariant();
|
||||
|
||||
if (!indexIsValid(idx))
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
const AvsSequence &seq = m_vecData.at(idx.row());
|
||||
switch (idx.column())
|
||||
{
|
||||
case Order:
|
||||
return idx.row() + 1;
|
||||
case Start:
|
||||
return msecsSinceEpochToStringx(std::chrono::milliseconds(seq.start.time_since_epoch()).count());
|
||||
case End:
|
||||
return msecsSinceEpochToStringx(std::chrono::milliseconds((seq.start+seq.duration).time_since_epoch()).count());
|
||||
case Duration:
|
||||
{
|
||||
qint64 duration = std::chrono::milliseconds(seq.duration).count();
|
||||
qint64 durationDisplay = duration / seq.speed * 100;
|
||||
|
||||
qint64 secs = durationDisplay / 1000;
|
||||
qint64 msecs = (durationDisplay % 1000);
|
||||
|
||||
return QStringLiteral("%1.%2").arg(secs).arg(msecs, 3, 10, QLatin1Char('0'));
|
||||
}
|
||||
case Description:
|
||||
return seq.description;
|
||||
case Streams:
|
||||
return seq.internalStreamName;
|
||||
case Volume:
|
||||
return QVariant(seq.volume);
|
||||
case Speed:
|
||||
return QVariant(seq.speed);
|
||||
}
|
||||
}
|
||||
else if (role == Qt::BackgroundRole)
|
||||
{
|
||||
if (m_highlightedRows.contains(idx.row()))
|
||||
return QColor(Qt::green);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void AvmSequenceModel::setHighlightedRow(int highlighted, bool reset)
|
||||
{
|
||||
QVector<int> oldSelected = m_highlightedRows;
|
||||
|
||||
if (highlighted < 0)
|
||||
m_highlightedRows.clear();
|
||||
else if (reset)
|
||||
m_highlightedRows = { highlighted };
|
||||
else
|
||||
m_highlightedRows += highlighted;
|
||||
|
||||
if (m_highlightedRows == oldSelected)
|
||||
return; // no row data changed, nothing to do
|
||||
|
||||
QVector<int> changedRows;
|
||||
changedRows.reserve(oldSelected.size() + m_highlightedRows.size());
|
||||
|
||||
if (m_highlightedRows.isEmpty() || reset)
|
||||
for (int row : qAsConst(oldSelected))
|
||||
changedRows += row;
|
||||
|
||||
for (int row : qAsConst(m_highlightedRows))
|
||||
changedRows += row;
|
||||
|
||||
for (int row : qAsConst(changedRows))
|
||||
emit dataChanged(index(row, 0), index(row, Description), { Qt::BackgroundRole });
|
||||
}
|
||||
|
||||
void AvmSequenceProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
|
||||
{
|
||||
m_sourceModel = qobject_cast<AvmSequenceModel*>(sourceModel);
|
||||
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
|
||||
}
|
||||
|
||||
bool AvmSequenceProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||
{
|
||||
if (!m_sourceModel ||
|
||||
source_left.column() != source_right.column() ||
|
||||
!source_left.isValid() ||
|
||||
!source_right.isValid())
|
||||
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
|
||||
const auto &dataLeft = m_sourceModel->m_vecData.at(source_left.row());
|
||||
const auto dataRight = m_sourceModel->m_vecData.at(source_right.row());
|
||||
switch (source_left.column())
|
||||
{
|
||||
case AvmSequenceModel::Start:
|
||||
return std::chrono::milliseconds(dataLeft.start.time_since_epoch()).count() < std::chrono::milliseconds(dataRight.start.time_since_epoch()).count();
|
||||
case AvmSequenceModel::End:
|
||||
return std::chrono::milliseconds((dataLeft.start+dataLeft.duration).time_since_epoch()).count() < std::chrono::milliseconds((dataRight.start+dataLeft.duration).time_since_epoch()).count();
|
||||
case AvmSequenceModel::Order:
|
||||
case AvmSequenceModel::Speed:
|
||||
case AvmSequenceModel::Streams:
|
||||
case AvmSequenceModel::Volume:
|
||||
case AvmSequenceModel::Description:
|
||||
break;
|
||||
}
|
||||
|
||||
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - AVS Sequence Model
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2017-08-21
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LtsWidgets/lts_abstract_table_model.h>
|
||||
#include <LtsWidgets/lts_column_filter_proxy_model.h>
|
||||
#include <AudioVideoManagement/logger.h>
|
||||
#include <AudioVideoManagement/ava_types.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
class AvsSequence
|
||||
{
|
||||
public:
|
||||
QString internalStreamName;
|
||||
AvaSpeed speed{100};
|
||||
AvaVolume volume{0};
|
||||
AvaTimePoint start;
|
||||
AvaDuration duration;
|
||||
bool pint = false;
|
||||
QString description;
|
||||
};
|
||||
|
||||
|
||||
class AvmSequenceModel final : public LtsWidgets::LtsAbstractTableModel<AvsSequence>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Columns
|
||||
{
|
||||
Order = 0,
|
||||
Start,
|
||||
End,
|
||||
Duration,
|
||||
Streams,
|
||||
Speed,
|
||||
Volume,
|
||||
Description
|
||||
};
|
||||
|
||||
explicit AvmSequenceModel(QObject *parent = nullptr);
|
||||
void moveUp(const QModelIndex &idx);
|
||||
void moveDown(const QModelIndex &idx);
|
||||
bool initModel(const QJsonArray &data);
|
||||
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
|
||||
QJsonArray getSequencesAsJsonArray() const;
|
||||
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||
QVector<int> highlightedRows() const { return m_highlightedRows; }
|
||||
void setHighlightedRow(int highlighted, bool reset = true);
|
||||
|
||||
friend class AvmSequenceProxyModel;
|
||||
|
||||
public slots:
|
||||
void setTimezoneHoursOffset(int timezoneHoursOffset);
|
||||
|
||||
private:
|
||||
QString dateTimeToStringx(const QDateTime &dt) const;
|
||||
QString msecsSinceEpochToStringx(qint64 msecs) const;
|
||||
|
||||
QVector<int> m_highlightedRows;
|
||||
int m_timezoneHoursOffset = 0;
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
class AvmSequenceProxyModel final : public LtsWidgets::LtsColumnFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using LtsWidgets::LtsColumnFilterProxyModel::LtsColumnFilterProxyModel;
|
||||
void setSourceModel(QAbstractItemModel *sourceModel) override;
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
|
||||
private:
|
||||
AvmSequenceModel *m_sourceModel = nullptr;
|
||||
};
|
||||
}
|
||||
+684
@@ -0,0 +1,684 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Topic Editor for the Situation Topic in the Script Editor
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-04-04
|
||||
*/
|
||||
|
||||
#include "topic_editor.h"
|
||||
#include "ui_topic_editor.h"
|
||||
|
||||
#include <AudioVideoManagement/av_helper.h>
|
||||
#include <AudioVideoManagement/ava_comm.h>
|
||||
#include <AudioVideoManagement/ava_session.h>
|
||||
#include <AudioVideoManagement/avs_dialog.h>
|
||||
#include <AudioVideoManagement/Shared/playback_app.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
#include <TopicEditorBase/topic_previewer.h>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <cmath>
|
||||
|
||||
#include <QJsonArray>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
|
||||
#include <QScopeGuard>
|
||||
#endif
|
||||
|
||||
|
||||
static constexpr int s_defaultOffset = 10 * 1000; // 10s
|
||||
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
QString dateTimeToStringz(const QDateTime &dt)
|
||||
{
|
||||
return dt.toString(QStringLiteral("dd.MM.yy HH:mm:ss.zzz"));
|
||||
}
|
||||
|
||||
QString msecsSinceEpochToStringz(qint64 msecs)
|
||||
{
|
||||
return dateTimeToStringz(QDateTime::fromMSecsSinceEpoch(msecs));
|
||||
}
|
||||
|
||||
qint64 timePointToMsecs(const AvaTimePoint &atp)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(atp.time_since_epoch()).count();
|
||||
}
|
||||
|
||||
AvaDuration msecsToAvaDuration(qint64 msecs)
|
||||
{
|
||||
return std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(msecs));
|
||||
}
|
||||
|
||||
AvaTimePoint msecsToAvaTimePoint(qint64 msecs)
|
||||
{
|
||||
return AvaTimePoint(msecsToAvaDuration(msecs));
|
||||
}
|
||||
|
||||
|
||||
TopicEditor::TopicEditor(LtsSession::LtsSession *session, const TopicEditorBase::TopicData &data, QWidget *parent)
|
||||
: TopicEditorBase::TopicEditor(parent)
|
||||
, m_session(session)
|
||||
, m_avmDataIfc(nullptr)
|
||||
, m_model(new AvmSequenceModel(this))
|
||||
, m_playbackMenu(nullptr)
|
||||
, m_ui(new Ui::AVTopicEditor)
|
||||
, m_logger(getLogger("TopicEditor"))
|
||||
{
|
||||
Q_ASSERT(session);
|
||||
|
||||
m_ui->setupUi(this);
|
||||
m_ui->tvSequences->hideColumn(AvmSequenceModel::Order);
|
||||
init(m_ui->widget);
|
||||
|
||||
m_isAudio = (data.topicType == TopicEditorBase::TopicType::Audio);
|
||||
if (m_isAudio)
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Add_Audio_Topic"));
|
||||
else
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Add_Video_Topic"));
|
||||
|
||||
setTopicData(data);
|
||||
m_topicData.properties[QStringLiteral("IsAudio")] = bool(m_topicData.topicType == TopicEditorBase::TopicType::Audio);
|
||||
m_ui->widget->previewer()->setTopicData(m_topicData);
|
||||
|
||||
QJsonArray arr = getTopicVDataAsArray(QLatin1String("Sequences"));
|
||||
m_model->initModel(arr);
|
||||
m_ui->tvSequences->resizeColumnsToContents();
|
||||
refreshDurationText();
|
||||
m_proxyModel = new AvmSequenceProxyModel(this);
|
||||
m_proxyModel->setSourceModel(m_model);
|
||||
m_ui->tvSequences->setModel(m_proxyModel);
|
||||
connect(m_ui->tvSequences, SIGNAL(doubleClicked(QModelIndex)),
|
||||
m_ui->tvSequences->selectionModel(), SLOT(select(QModelIndex, QItemSelectionModel::Toggle)));
|
||||
|
||||
connect(m_ui->tvSequences->selectionModel(), &QItemSelectionModel::selectionChanged,
|
||||
this, &TopicEditor::onEnaDisa);
|
||||
connect(m_ui->pbDelete, &QToolButton::clicked,
|
||||
this, &TopicEditor::onDeleteSequences);
|
||||
connect(m_ui->pbUp, &QToolButton::clicked,
|
||||
this, &TopicEditor::onSequenceUp);
|
||||
connect(m_ui->pbDown, &QToolButton::clicked,
|
||||
this, &TopicEditor::onSequenceDown);
|
||||
connect(m_ui->pbPreview, &QPushButton::clicked, this, [this]()
|
||||
{
|
||||
m_playbackMenu->setStreamTime(m_ui->dtStart->msecsSinceEpoch());
|
||||
});
|
||||
|
||||
if (!m_session->getForeignDataInterface_NEW<AudioVideoManagement::DataInterface>(m_avmDataIfc))
|
||||
{
|
||||
playbackDlgNotAvailable(tr("The audio/video interface is not available in this session."));
|
||||
return;
|
||||
}
|
||||
|
||||
PlaybackApp *app = m_isAudio ? m_avmDataIfc->audioPlaybackApp() : m_avmDataIfc->videoPlaybackApp();
|
||||
if (app == nullptr)
|
||||
{
|
||||
playbackDlgNotAvailable(tr("The audio/video interface is currently not available."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (app->inUse())
|
||||
{
|
||||
playbackDlgNotAvailable(tr("The audio/video interface is already in use by another dialog."));
|
||||
return;
|
||||
}
|
||||
|
||||
// AFTER THIS POINT IT IS GUARANTEED TO HAVE A PLAYBACK DIALOG!
|
||||
|
||||
m_ui->pbApply->setEnabled(false);
|
||||
m_ui->pbDelete->setEnabled(false);
|
||||
m_ui->pbUp->setEnabled(false);
|
||||
m_ui->pbDown->setEnabled(false);
|
||||
|
||||
// playback menu
|
||||
boost::optional<QString> stream = boost::none; // will be used as stream preset
|
||||
if (m_topicData.properties.contains(QStringLiteral("stream")))
|
||||
stream = m_topicData.properties.value(QStringLiteral("stream")).toString();
|
||||
|
||||
// get preset start time from topic (e.g. from evaluation topic)
|
||||
qint64 startMsecs = 0;
|
||||
boost::optional<AvaTimePoint> start = boost::none; // will be used as current time preset
|
||||
{
|
||||
const QJsonValue jsonValue(getTopicDataValue(QStringLiteral("start")));
|
||||
if (jsonValue.isDouble())
|
||||
{
|
||||
startMsecs = static_cast<qint64>(jsonValue.toDouble());
|
||||
start = boost::optional<AvaTimePoint>(toTimePoint(startMsecs));
|
||||
}
|
||||
}
|
||||
|
||||
// get preset end time from topic (e.g. from evaluation topic)
|
||||
qint64 endMsecs = 0;
|
||||
{
|
||||
const QJsonValue jsonValue(getTopicDataValue(QStringLiteral("end")));
|
||||
if (jsonValue.isDouble())
|
||||
endMsecs = static_cast<qint64>(jsonValue.toDouble());
|
||||
}
|
||||
|
||||
m_playbackMenu = new AvmPlaybackMenu(this);
|
||||
m_playbackMenu->init(
|
||||
m_session, m_avmDataIfc->avaSession(), PlaybackAppHandle(app), m_avmDataIfc->streamNameMapping(), m_isAudio, true /* is inside topic editor */,
|
||||
stream, start
|
||||
);
|
||||
m_ui->playbackParent->layout()->addWidget(m_playbackMenu);
|
||||
//m_playbackDlg->hideStreamSelection();
|
||||
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::setTopicButtonStates,
|
||||
this, &TopicEditor::onSetTopicButtonStates);
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::markerSelected,
|
||||
this, &TopicEditor::onMarkerSelected);
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalCurrentStreamChanged, this, [this](const QString &displayName)
|
||||
{
|
||||
m_ui->lblStream->setText(displayName);
|
||||
});
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalSetStart, this, [this](qint64 start)
|
||||
{
|
||||
m_ui->dtStart->setMsecsSinceEpoch(start);
|
||||
if (m_ui->dtEnd->msecsSinceEpoch() < start)
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(start + s_defaultOffset);
|
||||
});
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalSetEnd, this, [this](qint64 end)
|
||||
{
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(end);
|
||||
if (m_ui->dtStart->msecsSinceEpoch() > end)
|
||||
m_ui->dtStart->setMsecsSinceEpoch(end - s_defaultOffset);
|
||||
});
|
||||
m_playbackMenu->show();
|
||||
|
||||
// NOTE: don't confuse the time *inside* the script timeline with a time since epoch!! following code lines do not make sense:
|
||||
|
||||
// // initial values
|
||||
if (m_topicData.startTime > -1)
|
||||
{
|
||||
m_ui->dtStart->setMsecsSinceEpoch(static_cast<qint64>(m_topicData.startTime) * 1000);
|
||||
if (m_topicData.duration > 0)
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(static_cast<qint64>(m_topicData.startTime + m_topicData.duration) * 1000);
|
||||
} // if
|
||||
|
||||
// the playback dialog part of the topic editor
|
||||
connect(m_ui->tvSequences, &QTableView::doubleClicked,
|
||||
this, &TopicEditor::onEditSequence);
|
||||
connect(m_ui->dtStart, &LtsWidgets::LtsMSecDateTimeEdit::dateTimeChanged,
|
||||
this, &TopicEditor::onTimeChanged);
|
||||
connect(m_ui->dtEnd, &LtsWidgets::LtsMSecDateTimeEdit::dateTimeChanged,
|
||||
this, &TopicEditor::onTimeChanged);
|
||||
connect(m_ui->pbAdd, &QToolButton::clicked,
|
||||
this, &TopicEditor::onAddSequence);
|
||||
connect(m_ui->pbApply, &QToolButton::clicked,
|
||||
this, &TopicEditor::onApplySequence);
|
||||
connect(m_ui->pbEditSequence, &QPushButton::clicked,
|
||||
this, &TopicEditor::onEditSequence);
|
||||
|
||||
// if start and end times were set (e.g. by evaluation topic), then auto set them as start & end time for the new sequence
|
||||
if (startMsecs > 0 && endMsecs > 0)
|
||||
{
|
||||
m_ui->dtStart->setMsecsSinceEpoch(startMsecs);
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(endMsecs);
|
||||
}
|
||||
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalTimezoneChanged,
|
||||
m_ui->dtStart, &LtsWidgets::LtsMSecDateTimeEdit::setTimezoneHoursOffset);
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalTimezoneChanged,
|
||||
m_ui->dtEnd, &LtsWidgets::LtsMSecDateTimeEdit::setTimezoneHoursOffset);
|
||||
connect(m_playbackMenu, &AvmPlaybackMenu::signalTimezoneChanged,
|
||||
m_model, &AvmSequenceModel::setTimezoneHoursOffset);
|
||||
|
||||
int offset = m_playbackMenu->currentOffset();
|
||||
m_model->setTimezoneHoursOffset(offset); // as the emit happens before the commits!
|
||||
m_ui->dtStart->setTimezoneHoursOffset(offset);
|
||||
m_ui->dtEnd->setTimezoneHoursOffset(offset);
|
||||
}
|
||||
|
||||
TopicEditor::~TopicEditor()
|
||||
{
|
||||
delete m_ui;
|
||||
delete m_model;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE
|
||||
|
||||
void TopicEditor::updateInternalData()
|
||||
{
|
||||
if (hasTopicDataProperties())
|
||||
{
|
||||
m_topicStartTime = getTopicDataValue(QStringLiteral("start")).toDouble();
|
||||
m_topicStreamName = getTopicDataValue(QStringLiteral("stream")).toString();
|
||||
QJsonArray arr = getTopicVDataAsArray(QLatin1String("Sequences"));
|
||||
m_model->initModel(arr);
|
||||
m_ui->tvSequences->resizeColumnsToContents();
|
||||
setDurationText(calcTrackLength(arr));
|
||||
}
|
||||
|
||||
m_ui->pbApply->setEnabled(false);
|
||||
m_ui->pbDelete->setEnabled(false);
|
||||
}
|
||||
|
||||
void TopicEditor::retranslate()
|
||||
{
|
||||
// translate(m_ui, trackEntryType() == TopicType::Audio ? tr("Audio Topic Editor") : tr("Video Topic Editor") );
|
||||
translate(m_ui, m_topicData.topicType == TopicEditorBase::TopicType::Audio ? tr("Audio Topic Editor") : tr("Video Topic Editor") );
|
||||
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->setText(tr("Apply to topic", "Auf Topic übernehmen"));
|
||||
}
|
||||
|
||||
|
||||
AvsSequence TopicEditor::createSequence(const AvaTimePoint &start, const AvaTimePoint &end) const
|
||||
{
|
||||
AvsSequence seq;
|
||||
|
||||
seq.start = start;
|
||||
seq.duration = end - start; // not sure if AVS needs duration or end
|
||||
seq.description = m_ui->leDescription->text();
|
||||
|
||||
if (m_playbackMenu != nullptr)
|
||||
{
|
||||
seq.speed = m_playbackMenu->getSpeed();
|
||||
seq.volume = m_playbackMenu->getVolume();
|
||||
seq.internalStreamName = m_playbackMenu->getInternalStreamName();
|
||||
seq.pint = m_playbackMenu->getSkipPause();
|
||||
}
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
AvsSequence TopicEditor::createCurrentSequence() const
|
||||
{
|
||||
return createSequence(AvaTimePoint(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(m_ui->dtStart->msecsSinceEpoch()))),
|
||||
AvaTimePoint(std::chrono::duration_cast<AvaDuration>(std::chrono::milliseconds(m_ui->dtEnd->msecsSinceEpoch()))));
|
||||
}
|
||||
|
||||
void TopicEditor::refreshDurationText()
|
||||
{
|
||||
setDurationText(calcTrackLength(m_model->getSequencesAsJsonArray()));
|
||||
}
|
||||
|
||||
void TopicEditor::setDurationText(qint64 length)
|
||||
{
|
||||
if (length > s_durationLimit)
|
||||
m_ui->lblDuration->setText(tr("Playback too long!"));
|
||||
else
|
||||
m_ui->lblDuration->setText(QTime::fromMSecsSinceStartOfDay(length).toString(QStringLiteral("HH:mm:ss.zzz")));
|
||||
}
|
||||
|
||||
qint64 TopicEditor::calcTrackLength(const QJsonArray &arr) const
|
||||
{
|
||||
qint64 length = 0;
|
||||
for (const auto &ele : arr)
|
||||
{
|
||||
const QJsonObject &seqObj = ele.toObject();
|
||||
if (seqObj.isEmpty())
|
||||
continue;
|
||||
// length += std::ceil(seqObj.value(QLatin1String("duration")).toDouble() * (std::fabs(seqObj.value(QLatin1String("speed")).toDouble(100)) / 100)); Original
|
||||
length += std::ceil(seqObj.value(QLatin1String("duration")).toDouble() / (std::fabs(seqObj.value(QLatin1String("speed")).toDouble(100))) * 100 );
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void TopicEditor::playbackDlgNotAvailable(const QString &msg)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, msg);
|
||||
m_ui->groupBox->hide();
|
||||
m_ui->line->hide();
|
||||
|
||||
// left side:
|
||||
m_ui->pbEditSequence->hide();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE SLOTS
|
||||
|
||||
bool TopicEditor::apply2()
|
||||
{
|
||||
const QJsonArray &arr = m_model->getSequencesAsJsonArray();
|
||||
qint64 duration = calcTrackLength(arr);
|
||||
|
||||
if (duration > s_durationLimit)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("This topic has exceeded the maximum length of %1 seconds.").arg(s_durationLimit));
|
||||
return false;
|
||||
}
|
||||
|
||||
setDurationText(duration);
|
||||
|
||||
if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("AVM.AllowIncompleteTopic")) != QVariant(true)) // don't execute code block if AllowIncompleteTopic is set !
|
||||
{
|
||||
if (duration <= 0)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(
|
||||
this, tr("The total playback length is less than 1 second long. <br>"
|
||||
"Please verify that you have one or more sequences in the sequences list and that these sequences are of at least 1 second length."),
|
||||
tr("Playback length too short")
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 secs = duration / 1000; // strip msecs ...
|
||||
qint64 msecs = duration - (secs * 1000); // ... and set to zero !
|
||||
qWarning() << "secs" << secs << "msecs" << msecs;
|
||||
|
||||
// a ratio between 500 missing mseconds of a video and 500 msecs of no video output seems balanced
|
||||
if (msecs >= 500)
|
||||
++secs;
|
||||
qWarning() << "secs now" << secs;
|
||||
m_topicData.duration = secs;
|
||||
}
|
||||
else // AllowIncompleteTopic is set! fake it!
|
||||
m_topicData.duration = 10;
|
||||
|
||||
setTopicDataProperty(QStringLiteral("Sequences"), m_model->getSequencesAsJsonArray());
|
||||
setTopicDataProperty(QStringLiteral("ScriptName"), QString(QStringLiteral("S") + QString::number(m_topicData.id)));
|
||||
setTopicDataProperty(QStringLiteral("IsAudio"), bool(m_topicData.topicType == TopicEditorBase::TopicType::Audio));
|
||||
setDescription(m_ui->widget->description());
|
||||
m_ui->widget->previewer()->setTopicData(m_topicData);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TopicEditor::onEditSequence()
|
||||
{
|
||||
if (!m_playbackMenu)
|
||||
return;
|
||||
|
||||
DEBUG_LOG(m_logger) << "slotEditSequence ";
|
||||
const auto selRows = m_ui->tvSequences->selectionModel()->selectedRows();
|
||||
|
||||
if (selRows.size() != 1)
|
||||
{
|
||||
m_model->setHighlightedRow(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
int row = selRows.at(0).row();
|
||||
m_model->setHighlightedRow(row);
|
||||
const AvsSequence selectedSeq = m_model->getRowData(row);
|
||||
|
||||
DEBUG_LOG(m_logger) << "selectedSeq.internalStreamName=" << selectedSeq.internalStreamName;
|
||||
m_playbackMenu->setStreamName(selectedSeq.internalStreamName);
|
||||
|
||||
m_ui->dtStart->setMsecsSinceEpoch(std::chrono::milliseconds(selectedSeq.start.time_since_epoch()).count());
|
||||
m_ui->dtEnd->setMsecsSinceEpoch(std::chrono::milliseconds((selectedSeq.start + selectedSeq.duration).time_since_epoch()).count());
|
||||
|
||||
m_ui->leDescription->setText(selectedSeq.description);
|
||||
m_playbackMenu->setStreamByName(selectedSeq.internalStreamName);
|
||||
m_playbackMenu->setSpeed(selectedSeq.speed);
|
||||
m_playbackMenu->setVolume(selectedSeq.volume);
|
||||
m_playbackMenu->setSkipPause(selectedSeq.pint);
|
||||
m_playbackMenu->setSavedPos(m_ui->dtStart->msecsSinceEpoch()); // plays that timepoint as soon as the stream was selected and QueryTimeline was successful not working.. : m_playbackDlg->setStreamTime(m_ui->dteStart->msecsSinceEpoch());
|
||||
|
||||
m_ui->pbApply->setEnabled(true);
|
||||
}
|
||||
|
||||
void TopicEditor::onTimeChanged()
|
||||
{
|
||||
/* user gets a warning on 'add' if times don't add up, otherwise there is too much movement in the end-time dte! */
|
||||
|
||||
|
||||
// LtsWidgets::LtsMSecDateTimeEdit *dtSender = qobject_cast<LtsWidgets::LtsMSecDateTimeEdit*>(sender());
|
||||
|
||||
// if (!dtSender)
|
||||
// return;
|
||||
|
||||
// if (dtSender == m_ui->dtEnd)
|
||||
// {
|
||||
// if (m_ui->dtEnd->msecsSinceEpoch() < m_ui->dtStart->msecsSinceEpoch())
|
||||
// {
|
||||
// qint64 duration = static_cast<qint64>(m_trackEntry->m_length > 0 ? m_trackEntry->m_length : s_defaultOffset) * 1000;
|
||||
// m_ui->dteStart->setMsecsSinceEpoch(m_ui->dteEnd->msecsSinceEpoch() - duration);
|
||||
// } // if
|
||||
// } // if
|
||||
// else // start is sender
|
||||
// {
|
||||
// if (m_ui->dteEnd->msecsSinceEpoch() < m_ui->dteStart->msecsSinceEpoch())
|
||||
// {
|
||||
// qint64 duration = static_cast<qint64>(m_trackEntry->m_length > 0 ? m_trackEntry->m_length : s_defaultOffset) * 1000;
|
||||
// m_ui->dteEnd->setMsecsSinceEpoch(m_ui->dteStart->msecsSinceEpoch() + duration);
|
||||
// } // if
|
||||
// } // else
|
||||
}
|
||||
|
||||
void TopicEditor::onApplySequence()
|
||||
{
|
||||
const auto seq = createCurrentSequence();
|
||||
if (seq.start.time_since_epoch().count() == 0 || seq.duration.count() == 0)
|
||||
return;
|
||||
|
||||
const auto rows = m_model->highlightedRows();
|
||||
|
||||
if (rows.count() != 1)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Please select a sequence from the sequence list first, then click 'Edit' to edit a sequence in the editor."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->setRowData(rows.first(), seq);
|
||||
|
||||
refreshDurationText();
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onAddSequence()
|
||||
{
|
||||
qint64 start = m_ui->dtStart->msecsSinceEpoch();
|
||||
qint64 end = m_ui->dtEnd->msecsSinceEpoch();
|
||||
|
||||
if (start == 0 || end == 0 || end < start)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Please check if start and end times are set and the end time is not lower than the start time."),
|
||||
tr("The start and/or end time you entered is invalid"));
|
||||
return;
|
||||
}
|
||||
|
||||
AvsSequence seq = createCurrentSequence();
|
||||
|
||||
if (seq.pint) // if empty parts should be skipped, the duration has to be calculated (as there can be more than 1 skipped / empty part in a sequence)
|
||||
{
|
||||
// async!
|
||||
|
||||
AvaTimePoint theStart = msecsToAvaTimePoint(m_ui->dtStart->msecsSinceEpoch());
|
||||
AvaTimePoint theEnd = msecsToAvaTimePoint(m_ui->dtEnd->msecsSinceEpoch());
|
||||
|
||||
QueryTimeline *qt = m_playbackMenu->queryTimeline(theStart, theEnd);
|
||||
|
||||
if (qt == nullptr)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Currently unable to request sequences from the audio / video system.<br />Please try again later."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
setOverlay(new QWidget());
|
||||
|
||||
|
||||
connect(qt, &QueryTimeline::signalTimelineComplete,
|
||||
this, [this, theStart, theEnd](const AvaTimeline &timeline)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
|
||||
auto unchangableReset = qScopeGuard([this] { unsetOverlay(); });
|
||||
#else
|
||||
std::shared_ptr<int> unchangeableReset(nullptr, [this](int *) { unsetOverlay(); });
|
||||
#endif
|
||||
|
||||
static const QString msgTitle(tr("Sequences needed to be determined for audio / video clips with skipped silence"));
|
||||
const QVector<AvaTimelinePart> &parts = timeline.parts();
|
||||
|
||||
if (parts.isEmpty())
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("No sequences could be found inside the specified range. <br />Please select another range that contains audio / video."),
|
||||
msgTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList partsStrings;
|
||||
|
||||
for (const AvaTimelinePart &part : parts)
|
||||
{
|
||||
AvaTimePoint start = part.first;
|
||||
AvaTimePoint end = part.second;
|
||||
|
||||
if (theStart > start)
|
||||
start = theStart;
|
||||
if (theEnd < end)
|
||||
end = theEnd;
|
||||
|
||||
qint64 duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
qint64 secs = duration / 1000;
|
||||
qint64 msecs = (duration % 1000);
|
||||
QString durationString(QStringLiteral("%1.%2").arg(secs).arg(msecs, 3, 10, QLatin1Char('0')));
|
||||
|
||||
partsStrings += QStringLiteral("<span>%1 - %2 (%3 %4)</span>").arg(msecsSinceEpochToStringz(std::chrono::milliseconds(start.time_since_epoch()).count()),
|
||||
msecsSinceEpochToStringz(std::chrono::milliseconds(end.time_since_epoch()).count()),
|
||||
durationString,
|
||||
tr("seconds"));
|
||||
}
|
||||
|
||||
|
||||
// Patrick said the message is not needed
|
||||
//#define AskUserSequenceList
|
||||
#ifdef AskUserSequenceList
|
||||
auto msg = new LtsWidgets::LtsMessageDialogNew(this, tr("The following sequences were determined: <br /> <br /> %1 <br /> <br />Do you want to add the listed sequences to the sequences list?").arg(partsStrings.join(QStringLiteral("<br />"))),
|
||||
msgTitle, LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::Cancel, { { LtsWidgets::Button::Yes, tr("Add") } });
|
||||
|
||||
connect(msg, &LtsWidgets::LtsMessageDialogNew::yes,
|
||||
this, [this, parts, theStart, theEnd]()
|
||||
#endif
|
||||
{
|
||||
m_model->setHighlightedRow(-1); // initial highlight reset
|
||||
|
||||
for (const AvaTimelinePart &part : parts)
|
||||
{
|
||||
AvaTimePoint start = part.first;
|
||||
AvaTimePoint end = part.second;
|
||||
|
||||
if (theStart > start)
|
||||
start = theStart;
|
||||
if (theEnd < end)
|
||||
end = theEnd;
|
||||
|
||||
m_model->insertRow(createSequence(start, end));
|
||||
int row = m_model->rowCount() - 1;
|
||||
QModelIndex idx = m_model->index(row, 0);
|
||||
|
||||
if (!idx.isValid())
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("One or more sequences could not be added to the sequences list. <br />Please try again later."),
|
||||
msgTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->setHighlightedRow(row, false /* add, don't reset after adding 1 of n rows */);
|
||||
m_ui->tvSequences->setCurrentIndex(idx);
|
||||
}
|
||||
|
||||
refreshDurationText();
|
||||
m_ui->pbApply->setEnabled(true); // new seq now editable
|
||||
slotChangesMade();
|
||||
}
|
||||
#ifdef AskUserSequenceList
|
||||
);
|
||||
#endif
|
||||
|
||||
});
|
||||
|
||||
return;
|
||||
} // if (seq.pint)
|
||||
|
||||
|
||||
if (seq.start.time_since_epoch().count() == 0 || seq.duration.count() == 0)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Start and end times are not valid. Please check if your input values are valid."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->insertRow(seq);
|
||||
|
||||
int row = m_model->rowCount() - 1;
|
||||
QModelIndex idx = m_model->index(row, 0);
|
||||
|
||||
if (!idx.isValid())
|
||||
return;
|
||||
|
||||
m_model->setHighlightedRow(row);
|
||||
m_ui->tvSequences->setCurrentIndex(idx);
|
||||
|
||||
refreshDurationText();
|
||||
m_ui->pbApply->setEnabled(true); // new seq now editable
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onDeleteSequences()
|
||||
{
|
||||
const auto selRows = m_ui->tvSequences->selectionModel()->selectedRows();
|
||||
QVector<int> rowIds;
|
||||
rowIds.reserve(selRows.size());
|
||||
for (const QModelIndex &idx : selRows)
|
||||
rowIds += idx.row();
|
||||
std::sort(rowIds.begin(), rowIds.end(), std::greater<int>());
|
||||
|
||||
const auto rows = m_model->highlightedRows();
|
||||
int curEditedRow = -1;
|
||||
|
||||
if (rows.count() == 1)
|
||||
curEditedRow = rows.first();
|
||||
|
||||
for (int row : qAsConst(rowIds))
|
||||
{
|
||||
m_model->removeRow(row);
|
||||
if (curEditedRow == row)
|
||||
{
|
||||
curEditedRow = -1;
|
||||
m_model->setHighlightedRow(curEditedRow);
|
||||
m_ui->pbApply->setEnabled(false);
|
||||
onEnaDisa();
|
||||
}
|
||||
if (curEditedRow > row)
|
||||
--curEditedRow;
|
||||
}
|
||||
m_model->setHighlightedRow(curEditedRow);
|
||||
refreshDurationText();
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onSequenceUp()
|
||||
{
|
||||
m_model->moveUp(m_ui->tvSequences->currentIndex());
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onSequenceDown()
|
||||
{
|
||||
m_model->moveDown(m_ui->tvSequences->currentIndex());
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void TopicEditor::onSetTopicButtonStates(bool enable)
|
||||
{
|
||||
if (enable && m_topicStartTime)
|
||||
m_ui->dtStart->setMsecsSinceEpoch(m_topicStartTime);
|
||||
}
|
||||
|
||||
void TopicEditor::onEnaDisa()
|
||||
{
|
||||
|
||||
const auto selRows = m_ui->tvSequences->selectionModel()->selectedRows();
|
||||
const bool hasSelection = (selRows.size() != 0);
|
||||
const bool hasSingleSelection = (selRows.size() == 1);
|
||||
|
||||
m_ui->pbEditSequence->setEnabled(hasSingleSelection);
|
||||
m_ui->pbDelete->setEnabled(hasSelection);
|
||||
m_ui->pbDown->setEnabled(hasSingleSelection);
|
||||
m_ui->pbUp->setEnabled(hasSingleSelection);
|
||||
}
|
||||
|
||||
void TopicEditor::onMarkerSelected(const QString &description)
|
||||
{
|
||||
m_ui->leDescription->setText(description);
|
||||
}
|
||||
|
||||
} // AudioVideoManagement FileManagement
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Topic Editor for the Situation Topic in the Script Editor
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-04-04
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "avm_sequence_model.h"
|
||||
#include "../avm_playback_menu.h"
|
||||
|
||||
#include <AudioVideoManagement/ava_control.h>
|
||||
#include <AudioVideoManagement/logger.h>
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
|
||||
#include <TopicEditorBase/topic_editor_base.h>
|
||||
|
||||
|
||||
namespace Ui { class AVTopicEditor; }
|
||||
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
class TopicEditor : public TopicEditorBase::TopicEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TopicEditor(LtsSession::LtsSession *session, const TopicEditorBase::TopicData &data, QWidget *parent = nullptr);
|
||||
~TopicEditor();
|
||||
|
||||
private:
|
||||
void updateInternalData() override final;
|
||||
void retranslate() override final;
|
||||
|
||||
AvsSequence createSequence(const AvaTimePoint &start, const AvaTimePoint &end) const;
|
||||
AvsSequence createCurrentSequence() const;
|
||||
void refreshDurationText();
|
||||
void setDurationText(qint64 length);
|
||||
qint64 calcTrackLength(const QJsonArray &arr) const;
|
||||
void playbackDlgNotAvailable(const QString &msg);
|
||||
|
||||
private slots:
|
||||
bool apply2() override final;
|
||||
|
||||
void onEditSequence();
|
||||
void onTimeChanged();
|
||||
void onApplySequence();
|
||||
void onAddSequence();
|
||||
void onDeleteSequences();
|
||||
void onSequenceUp();
|
||||
void onSequenceDown();
|
||||
void onSetTopicButtonStates(bool enable);
|
||||
void onEnaDisa();
|
||||
void onMarkerSelected(const QString &description);
|
||||
|
||||
private:
|
||||
const int s_durationLimit{86400000};
|
||||
|
||||
LtsSession::LtsSession *m_session{nullptr};
|
||||
std::shared_ptr<AudioVideoManagement::DataInterface> m_avmDataIfc{nullptr};
|
||||
|
||||
AvmSequenceModel *m_model{nullptr};
|
||||
AvmSequenceProxyModel *m_proxyModel{nullptr};
|
||||
AvmPlaybackMenu *m_playbackMenu{nullptr};
|
||||
|
||||
bool m_isAudio{false};
|
||||
qint64 m_topicStartTime{0};
|
||||
QString m_topicStreamName;
|
||||
|
||||
Ui::AVTopicEditor *m_ui{nullptr};
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AVTopicEditor</class>
|
||||
<widget class="QWidget" name="AVTopicEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1150</width>
|
||||
<height>577</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Script Editor</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1" rowspan="2">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbxSequences">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sequences:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lbxTopicDuration">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Topic duration:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblDuration">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="3">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="3">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QToolButton" name="pbDown">
|
||||
<property name="toolTip">
|
||||
<string>Sequence Down</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/sort-down.svg</normaloff>:/style/sort-down.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QToolButton" name="pbUp">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/sort-up.svg</normaloff>:/style/sort-up.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="pbDelete">
|
||||
<property name="toolTip">
|
||||
<string>Delete selected sequence from list</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete sequence</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/delete.svg</normaloff>:/style/delete.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QPushButton" name="pbEditSequence">
|
||||
<property name="toolTip">
|
||||
<string>Edit selected sequence from list in the right side of the layout</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit sequence</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/document-edit.svg</normaloff>:/style/document-edit.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="4" colspan="3">
|
||||
<widget class="QTableView" name="tvSequences">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="TopicEditorBase::TopicPreviewWidget" name="widget" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="2" rowspan="2">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Edit / Create sequence</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0,0,0">
|
||||
<item>
|
||||
<widget class="QWidget" name="playbackParent" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbxStrean">
|
||||
<property name="text">
|
||||
<string>Stream:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QLabel" name="lbxEndTime">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>End Time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="8">
|
||||
<widget class="QPushButton" name="pbPreview">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set as current time</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="lbxDescription">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Description:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbxStartTime">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start Time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="6">
|
||||
<widget class="LtsWidgets::LtsMSecDateTimeEdit" name="dtEnd"/>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QLabel" name="lblStream">
|
||||
<property name="text">
|
||||
<string notr="true">-</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="7">
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="5" colspan="4">
|
||||
<widget class="QLineEdit" name="leDescription"/>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<spacer name="horizontalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="LtsWidgets::LtsMSecDateTimeEdit" name="dtStart"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbApply">
|
||||
<property name="text">
|
||||
<string>Apply changes to sequence in list</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/dialog-ok-apply.svg</normaloff>:/style/dialog-ok-apply.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbAdd">
|
||||
<property name="text">
|
||||
<string>Append as new sequence to list</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/document-new.svg</normaloff>:/style/document-new.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TopicEditorBase::TopicPreviewWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>TopicEditorBase/topic_preview_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LtsWidgets::LtsMSecDateTimeEdit</class>
|
||||
<extends>QDateTimeEdit</extends>
|
||||
<header>LtsWidgets/lts_msec_date_time_edit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>tvSequences</tabstop>
|
||||
<tabstop>pbUp</tabstop>
|
||||
<tabstop>pbDown</tabstop>
|
||||
<tabstop>pbDelete</tabstop>
|
||||
<tabstop>pbEditSequence</tabstop>
|
||||
<tabstop>leDescription</tabstop>
|
||||
<tabstop>pbPreview</tabstop>
|
||||
<tabstop>pbApply</tabstop>
|
||||
<tabstop>pbAdd</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
+323
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Radio TreeModel
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
// Cameras are relevant for both, audio and video
|
||||
// The problem for audio here is that we cannot know yet whether later
|
||||
// there will be a camera mounted that is capable of audio.
|
||||
if (se.mountingPointTypeId() == InfrastructureManagement::MountingPointType::IndoorCamera ||
|
||||
se.mountingPointTypeId() == InfrastructureManagement::MountingPointType::OutdoorCamera)
|
||||
return true;
|
||||
|
||||
if (isAudio && se.mountingPointTypeId() == InfrastructureManagement::MountingPointType::AnnouncementUnit)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
||||
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;
|
||||
} // switch
|
||||
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
|
||||
{
|
||||
// Use display role (textual name of stream) for identification when dropping mimeData
|
||||
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";
|
||||
// build tree
|
||||
// Ignore NOT installed streams
|
||||
m_rootItem->removeAllChildren();
|
||||
// group to bundle streams
|
||||
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;
|
||||
|
||||
//check for mounted
|
||||
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;
|
||||
// What about id and parentId?
|
||||
|
||||
if (mountingPoint.mountingPointTypeId() == InfrastructureManagement::MountingPointType::IndoorCamera)
|
||||
toAddIndoor.push_back(item);
|
||||
else
|
||||
toAddOutdoor.push_back(item);
|
||||
// m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//add MVS in video list
|
||||
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(); // negative id for mvs
|
||||
item.isGroup = false;
|
||||
|
||||
// data.setValue(item);
|
||||
toAddMvs.push_back(item);
|
||||
//m_rootItem->appendChild(new NGLTreeItem(data, QIcon()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//sort
|
||||
|
||||
// using object as comp
|
||||
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;
|
||||
}
|
||||
|
||||
} // Namespace AudioVideoManagement
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Radio TreeModel
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#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;
|
||||
|
||||
/**
|
||||
* Used by AvailableStreamsModel and UsedStreamsModel.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used by MenuEdit.
|
||||
*
|
||||
* @see UsedStreamsModel
|
||||
*/
|
||||
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;
|
||||
};
|
||||
} // namespace AudioVideoManagement
|
||||
Q_DECLARE_METATYPE(AudioVideoManagement::StreamModelData)
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* AudioVideoManagement - Menu Struct Edit Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by hjl, SLSE, 2016-10-17
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* AudioVideoManagement - Menu Struct Edit Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by hjl, SLSE, 2016-10-17
|
||||
*/
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
} //namespace AudioVideoManagement
|
||||
@@ -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,159 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* AudioVideoManagement - Static Meteo Data Edit Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by hjl, SLSE, 2016-10-17
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
} // end of ctor()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
MenuEdit::~MenuEdit()
|
||||
{
|
||||
delete m_ui;
|
||||
} // end of dtor()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
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));
|
||||
} // end of retranslate()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
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());
|
||||
|
||||
// Z. Zt. keine Aufloesung nach Add/Edit verfuegbar
|
||||
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;
|
||||
} // end of init()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool MenuEdit::apply()
|
||||
{
|
||||
MenuStruct ms = m_usedStreamsModel->getMenuStruct();
|
||||
ms.isAudio(m_isAudio);
|
||||
|
||||
bool ret = m_requestHelper->startSingleRequest([this, ms]()
|
||||
{
|
||||
// Send modified data via FTC call
|
||||
return m_ftc->modifyMenuStructRequest(ms, this);
|
||||
});
|
||||
|
||||
return ret;
|
||||
} // end of accept()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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()); // TODO: muss hier nicht auch etwas aus dem dds geloescht werden? (mw)
|
||||
}
|
||||
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()); // TODO: muss hier nicht auch etwas aus dem dds geloescht werden? (mw)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* AudioVideoManagement - Menu Struct Edit Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by hjl, SLSE, 2016-10-17
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
//virtual functions from AudioVideoManagement::Interface::Ftc::ModifyMenuStructReplyHandler
|
||||
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; ///< last dds menu struct
|
||||
AudioVideoManagement::MenuStruct m_modifiedMenuStruct; ///< modified menu struct
|
||||
};
|
||||
|
||||
} //namespace AudioVideoManagement
|
||||
@@ -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>
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Gui Plugin for AudioVideoManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2016-10-05
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
// That's not how it works! Use getDtc instead
|
||||
m_dtc = m_avmDataIfc->getDtc();
|
||||
m_ftc = m_avmDataIfc->getFtc();
|
||||
|
||||
// only load on demand!!
|
||||
// if (!m_session->getDtc<InfrastructureManagement::DataInterface>(m_ismDtc))
|
||||
// return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::clearInterfaces()
|
||||
{
|
||||
//m_ismDtc = nullptr;
|
||||
m_ftc = nullptr;
|
||||
m_dtc = nullptr;
|
||||
m_avmDataIfc = nullptr;
|
||||
}
|
||||
|
||||
|
||||
QString InterfaceImpl::menuGroup() const
|
||||
{
|
||||
return tr("Audio / Video");
|
||||
}
|
||||
|
||||
|
||||
QString InterfaceImpl::playbackMenuGroup() const // Restpunkt - Fenster sollen gleichen Tab Parent Namen haben, wie im Menue einsortiert
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// TBC Rechte !!
|
||||
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);
|
||||
};
|
||||
|
||||
// TODO: apply user settings with single-action
|
||||
|
||||
actions.append(plData1);
|
||||
}
|
||||
|
||||
|
||||
// The following actions are only available when we have access to AVS.
|
||||
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(); // Restpunkt - Fenster sollen gleichen Tab Parent Namen haben, wie im Menue einsortiert
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//Topic
|
||||
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))
|
||||
{
|
||||
//dlg->avmPlaybackMenu()->startPlayback(true);
|
||||
return (m_session->focusTab(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab));
|
||||
}
|
||||
|
||||
/* NOTE: no need to trigger a startPlayback here, as the playback stuff is not initialized yet (only after
|
||||
* the timeline was received via signals/slots, so there is time to set some presets beforehand).
|
||||
* After init was (automatically) called, the presets gets automatically played (but only if any preset is set).*/
|
||||
|
||||
// did not exist before:
|
||||
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))
|
||||
{
|
||||
//dlg->avmPlaybackMenu()->startPlayback(true);
|
||||
return (m_session->focusTab(menuGroupString, identifier, LtsWidgets::LayoutArea::eDialogTab));
|
||||
}
|
||||
|
||||
/* NOTE: no need to trigger a startPlayback here, as the playback stuff is not initialized yet (only after
|
||||
* the timeline was received via signals/slots, so there is time to set some presets beforehand).
|
||||
* After init was (automatically) called, the presets gets automatically played (but only if any preset is set).*/
|
||||
|
||||
// did not exist before:
|
||||
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);//m_session, m_avmDataIfc->avaSession(), PlaybackAppHandle(app), m_avmDataIfc->streamNameMapping(), 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);
|
||||
}
|
||||
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Gui Plugin for AudioVideoManagement
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2016-10-05
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
//ScriptEditor
|
||||
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;
|
||||
//std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
|
||||
bool m_importDialogVisible{false};
|
||||
QPointer<AvmPlaybackMenuDialog> m_avmPlaybackMenuDialogAudio;
|
||||
QPointer<AvmPlaybackMenuDialog> m_avmPlaybackMenuDialogVideo;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - Menu Edit Overview Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2016-10-05
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
// connects fuer Widgetaenderungen
|
||||
connect(m_ui->widget, &LtsDataView::LtsDataView::signalSelectionChanged,
|
||||
this, &MenuEditView::onSelectionChanged);
|
||||
|
||||
connect(m_ui->widget, &LtsDataView::LtsDataView::signalDoubleClicked,
|
||||
this, &MenuEditView::onEditMenuStruct);
|
||||
|
||||
// Edit-Button erzeugen
|
||||
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);
|
||||
|
||||
} // end of ctor()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
MenuEditView::~MenuEditView()
|
||||
{
|
||||
delete m_ui;
|
||||
} // end of dtor()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void MenuEditView::retranslate()
|
||||
{
|
||||
if (m_isAudio)
|
||||
translate(m_ui, tr("Audio Menu"));
|
||||
else
|
||||
translate(m_ui, tr("Video Menu"));
|
||||
} // end of retranslate()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
|
||||
// Save/Load from Library Button
|
||||
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;
|
||||
} // end of init()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
|
||||
} // end of onAddMenuStruct()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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()
|
||||
{
|
||||
//LtsColumnOrganiser
|
||||
m_ui->widget->setColumnsOrganiserEnabled(identifier(), true);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// library
|
||||
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() ";
|
||||
}
|
||||
}
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - Menu Edit Overview Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2016-10-05
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -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,157 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - Weather Station Overview Dialog Model
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2016-10.05
|
||||
*/
|
||||
|
||||
#include "avm_menu_view_model.h"
|
||||
#include <FileManagement/flm_translation.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
//---------------------------------
|
||||
// MenuViewModel
|
||||
//---------------------------------
|
||||
|
||||
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);
|
||||
|
||||
// insert Model
|
||||
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());
|
||||
|
||||
//menuStruct.menuEntries():
|
||||
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
|
||||
{
|
||||
// already checked that content is not empty, parseable and contains proper XmlType
|
||||
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());
|
||||
|
||||
// Start menuStruct.menuEntries():
|
||||
|
||||
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;
|
||||
}
|
||||
// End menuStruct.menuEntries():
|
||||
result.append(menuStruct);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - Menu Edit Dialog Model
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2016-10.05
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AudioVideoManagement/avm_dtc.h>
|
||||
#include <AudioVideoManagement/logger.h>
|
||||
|
||||
#include <LtsWidgets/lts_abstract_table_model.h>
|
||||
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
// --------------------------------------------------------
|
||||
// MenuViewModel
|
||||
// --------------------------------------------------------
|
||||
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;
|
||||
};
|
||||
} //namespace AudioVideoManagement
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - New Name Frameless Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hanna E. Rathje, SLSE, 2016-11-21
|
||||
*/
|
||||
|
||||
#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); // TODO: undo workaround !
|
||||
m_ui->ltsDialogButtonBox->connectButton(LtsWidgets::DialogButton::Add, this, &NewMenuStruct::onSetNewName);
|
||||
}
|
||||
|
||||
NewMenuStruct::~NewMenuStruct()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
//SLOTS
|
||||
|
||||
void NewMenuStruct::onSetNewName()
|
||||
{
|
||||
if (m_ui->leName->text().isEmpty())
|
||||
return;
|
||||
emit newName(m_ui->leName->text());
|
||||
close();
|
||||
}
|
||||
|
||||
} //namespace ParticipantManagement
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* HMI Plugin for AudioVideoManagement - New Name Frameless Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hanna E. Rathje, SLSE, 2016-11-21
|
||||
*/
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -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>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* AvmPlaybackMenu
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Fröbel, SLSE, 2017-06-26
|
||||
*/
|
||||
|
||||
#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:
|
||||
//void slotTimelineComplete(const AvaTimeline &timeline);
|
||||
|
||||
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(AvaTimePoint t);
|
||||
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 selectedStreamName() const;
|
||||
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);
|
||||
|
||||
// both for topic editor
|
||||
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(/*int32_t mountingPointId, const QString &internalName, const QString &displayName*/);
|
||||
void onAddStreamClip();
|
||||
void onAddStreamMarker();
|
||||
void onRemoveSelectedStreams();
|
||||
void onTabChanged(int index);
|
||||
void onPauseFromPlaybackApp();
|
||||
void onResumeFromPlaybackApp();
|
||||
|
||||
|
||||
private:
|
||||
enum class ButtonStates
|
||||
{
|
||||
AllOff, //!< initial state when no stream is selected or when disconnected
|
||||
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();
|
||||
//void onStreamListReceived(const QVector<AvaStream> &streams);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -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>
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* AvmPlaybackMenu Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, NLE, 2020-03-16
|
||||
*/
|
||||
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* AvmPlaybackMenu Dialog
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, NLE, 2020-03-16
|
||||
*/
|
||||
|
||||
#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;
|
||||
//LOGGER_T m_logger;
|
||||
};
|
||||
} // namespace AudioVideoManagement
|
||||
+24
@@ -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>
|
||||
+686
@@ -0,0 +1,686 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Radio TreeModel
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#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;
|
||||
} // switch
|
||||
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();
|
||||
|
||||
//DEBUG_LOG(m_logger) << "row: " << row << ", column: " << column << ", parent: " << parent.row();
|
||||
// insert or append (or replace ?) rows :
|
||||
int beginRow;
|
||||
QModelIndex parentR;
|
||||
if (row != -1)
|
||||
{
|
||||
// insert rows:
|
||||
parentR = parent;
|
||||
beginRow = row;
|
||||
}
|
||||
else if (parent.isValid())
|
||||
{
|
||||
// parent is current item:
|
||||
// if(canHaveChildren(parent)) :
|
||||
parentR = parent;
|
||||
// beginRow = 0; prepend?
|
||||
beginRow = rowCount(parentR); // append to last child
|
||||
}
|
||||
else
|
||||
{
|
||||
// no parent, so append row: (take the toplevel parent)
|
||||
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)
|
||||
{
|
||||
//fixup data
|
||||
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) // do not drop group on group
|
||||
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; // no QSet, must be sorted!
|
||||
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);
|
||||
|
||||
// find the correct row to insert the data in respect to rowId
|
||||
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
|
||||
{
|
||||
//Check for duplicates:msSource
|
||||
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
|
||||
{
|
||||
/*
|
||||
* WHY ? You can use the Stream in different Menus aren't you?
|
||||
* You just need check if its in it CURRENT Dropped parent
|
||||
* First check if we drop to a folder or directly
|
||||
* If it's a folder DONT Check the parrent
|
||||
* We still need to allow reordering
|
||||
*/
|
||||
//check alle verschiedenen
|
||||
|
||||
NGLTreeItem *itemToCheck = getItemToCheck(index, parentIndex);
|
||||
|
||||
//const QVariant &variantCheck = itemToChek->data();
|
||||
// const StreamModelData &data = data->
|
||||
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; //Contains icons
|
||||
while (!stream.atEnd())
|
||||
{
|
||||
stream >> idParent >> idItem >> itemData;
|
||||
if (containsIcon)
|
||||
stream >> tmp;
|
||||
items.push_back(itemData.value<StreamModelData>());
|
||||
}
|
||||
|
||||
|
||||
//Size check
|
||||
int countDropped = 0; //count to be dropped
|
||||
int countAgainst = 0; //count in folder
|
||||
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; // prefer pre-increment operator
|
||||
}
|
||||
|
||||
QList<qint64> movedFolders;
|
||||
bool onlyOneFolder = false;
|
||||
for (StreamModelData &item : items)
|
||||
{
|
||||
|
||||
StreamModelData msToCheck = itemToCheck->data().value<StreamModelData>();
|
||||
if (movedFolders.size() == 0)
|
||||
{
|
||||
//first item in list, at parentID of current item
|
||||
movedFolders.push_back(item.parentId);
|
||||
}
|
||||
|
||||
if ((item.isGroup) && movedFolders.contains(item.parentId))
|
||||
movedFolders.push_back(item.id);
|
||||
//different sub folder than top level, but not in case of copying from left to right view (Why is ParentId in those cases the ID of the folder
|
||||
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;
|
||||
}
|
||||
//Check if only items from the same folder (including the folder are moved) (Not checked when copying from left to right view)
|
||||
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)) //Moving item in same folder, dont count them
|
||||
continue;
|
||||
++countDropped; // prefer pre-increment operator
|
||||
}
|
||||
|
||||
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_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::streamNameChanged,
|
||||
// this, &UsedStreamsModel::onStreamNameDataChanged);
|
||||
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>();
|
||||
// need to be adjusted later on /Yeah BUT WHY Did you never DONE it , well we will never know
|
||||
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;
|
||||
}
|
||||
//////////////////////
|
||||
// Recursively call the function for each child node.
|
||||
updateStreamNames(mountingPointId, newName, item->child(i));
|
||||
}
|
||||
}
|
||||
|
||||
void UsedStreamsModel::fixUpData(NGLTreeItem *item)
|
||||
{
|
||||
//ERROR_LOG(m_logger) << "FIXUP ";
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Radio TreeModel
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#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;
|
||||
|
||||
/**
|
||||
* Used by MenuEdit.
|
||||
*
|
||||
* @see AvailableStreamsModel.
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* IntervalModel
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Fröbel, SLSE, 2017-07-19
|
||||
*/
|
||||
|
||||
#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}); // TODO: only affected columns !!
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* forceSignal == true is used in a reconnect scenario.
|
||||
* Then, we call this method with pos == m_currentPos but
|
||||
* rely on the currentIntervalChanged signal in order
|
||||
* to update the button states (backward and forward interval).
|
||||
*/
|
||||
void IntervalModel::setCurrentPosition(AvaTimePoint pos, bool forceSignal)
|
||||
{
|
||||
if (m_vecData.isEmpty()) // can happen after a reconnect
|
||||
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);
|
||||
|
||||
|
||||
// Do something like in a CD player:
|
||||
// If we're a "good" time within the song, go to the beginning of the song.
|
||||
// Otherwise (if we're right at the beginning), go to the previous song.
|
||||
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)
|
||||
{
|
||||
// position is between current and previous interval.
|
||||
return 2 * i - 1;
|
||||
}
|
||||
|
||||
if (pos >= interval.first && pos <= interval.second)
|
||||
{
|
||||
// found containing interval
|
||||
return 2 * i;
|
||||
}
|
||||
}
|
||||
|
||||
// position is after last interval
|
||||
return 2 * m_vecData.size() - 1;
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* IntervalModel
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Fröbel, SLSE, 2017-07-19
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AudioVideoManagement/ava_types.h>
|
||||
#include <LtsWidgets/lts_abstract_table_model.h>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
|
||||
namespace AudioVideoManagement
|
||||
{
|
||||
|
||||
/**
|
||||
* Model for the Timeline so it can be shown in a TabView.
|
||||
* This is a poor man's version as the timeline is displayed in a table.
|
||||
* Later it will probably be replaced by something more usable.
|
||||
* @author Christian Fröbel
|
||||
*/
|
||||
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; }
|
||||
|
||||
/**
|
||||
* If the current position is within an interval:
|
||||
* - and the position is at least 2s into the interval, return that
|
||||
* - otherwise return the previous interval (unless there is none).
|
||||
* If the current position is between two intervals return the one before
|
||||
* the current position.
|
||||
*/
|
||||
const AvaTimelinePart &previousInterval() const;
|
||||
const AvaTimelinePart &nextInterval() const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Emitted if the current interval changed either due to
|
||||
* setCurrentPosition() or reset().
|
||||
* There must not always be a current interval as the current position
|
||||
* can also be between two intervals.
|
||||
* @param rowIndex might be invalid if the current position is between intervals.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* A HalfIndex denotes the current position within or between intervals.
|
||||
* The counting of indices is done as follows:
|
||||
* -1 0 1 2 3 4 5 6 7
|
||||
* ----- ----- ----- ----- # intervals 0...3
|
||||
* Examples:
|
||||
* within interval with index i => halfIndex = 2*i
|
||||
* before interval with index i => halfIndex = 2*i-1
|
||||
* after interval with index i => halfIndex = 2*i+1
|
||||
*/
|
||||
using HalfIndex = int;
|
||||
HalfIndex intervalForPos(AvaTimePoint pos) const;
|
||||
|
||||
boost::optional<AvaTimePoint> m_currentPos;
|
||||
boost::optional<HalfIndex> m_currentInterval;
|
||||
|
||||
int m_timezoneHoursOffset = 0;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
@@ -0,0 +1,476 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* TopicReplay
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Fröbel, NLSE, 2018-06-26
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
// test avs before using it after the
|
||||
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;
|
||||
}
|
||||
|
||||
/* init anyway as the geometry changes whenever..
|
||||
* if (!m_isAudio && !m_dialogGeometry)
|
||||
{
|
||||
//ERROR_LOG(m_logger) << "is video topic but no screen allocated for playback!";
|
||||
ERROR_LOG(m_logger) << "is video topic but no screen allocated for playback!";
|
||||
return false; // video topic but no screen allocated for playback
|
||||
}*/
|
||||
|
||||
|
||||
if (!m_dialogGeometry)
|
||||
{
|
||||
if (m_isAudio)
|
||||
m_appHandle = PlaybackAppHandle(app); // don't give geo for audio, otherwise avs is advised to create a window for audio, but a window is not needed for audio
|
||||
else
|
||||
m_appHandle = PlaybackAppHandle(app, QRect(0, 0, 0, 0)); // prevent the PlaybackApp to ask MMM for a selection by providing an invalid geo (puts the window at the last known position)
|
||||
}
|
||||
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 // Server
|
||||
{
|
||||
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)
|
||||
{
|
||||
/*DEBUG_LOG*/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()) // playbackInit needs to create an AppHandle before we can use it, otherwise we would aquire the app at this point an playbackInit has no chance to work with the app because we aquired it here (NOTE: aquiring an app blocks it from being modified, etc)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
|
||||
|
||||
#include "topic_replay.moc"
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* TopicReplay
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Fröbel, NLSE, 2018-06-26
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
/**
|
||||
* Helper class used to determine where the AVS dialog for presentation shall be placed.
|
||||
* An instance of this class will be returned by TopicReplay::createWidget().
|
||||
* The ScriptPlayer will then place (and resize) this widget accordingly.
|
||||
*/
|
||||
class ScreenPlacementHelper : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScreenPlacementHelper(QWidget *parent = nullptr)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setWindowTitle(QStringLiteral("Topic Replay Screen Placement Helper"));
|
||||
|
||||
// NOTE: the widget has to be visible in order to receive resizeEvents !!
|
||||
|
||||
// make widget transparent so that it's not disturbing
|
||||
setWindowFlags(Qt::FramelessWindowHint/* | Qt::Popup*/);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
activateWindow(); // force it into a window
|
||||
show();
|
||||
}
|
||||
|
||||
protected:
|
||||
void moveEvent(QMoveEvent *) override
|
||||
{
|
||||
emitNewGeometry();
|
||||
}
|
||||
|
||||
void resizeEvent(QResizeEvent *) override
|
||||
{
|
||||
emitNewGeometry();
|
||||
}
|
||||
|
||||
void emitNewGeometry()
|
||||
{
|
||||
//QRect globalGeometry(mapToGlobal(geometry().topLeft()), size());
|
||||
emit widgetPlaced(geometry());//globalGeometry);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* @return nullptr for audio topics.
|
||||
*/
|
||||
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 ScreenPlacementHelper;
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace AudioVideoManagement
|
||||
Reference in new issue
Block a user