Files
qt5-legatus-legacy/Lts/Plugins/AudioVideoManagement/Gui/AVMenuEdit/interval_model.cpp
T
2026-08-13 18:33:10 +02:00

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