/** * @RDE_HEADER@ * IntervalModel * $Id$ * $HeadURL$ * $Author$ * Created by Christian Fröbel, SLSE, 2017-07-19 */ #include "interval_model.h" #include #include #include #include #include #include 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