185 lines
4.6 KiB
C++
Executable File
185 lines
4.6 KiB
C++
Executable File
/**
|
|
* @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
|