Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
Executable
+772
@@ -0,0 +1,772 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* MVC model
|
||||
* $Id: TimelineModel.cpp 52154 2020-09-17 09:29:34Z wille_m $
|
||||
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/hotline/Lts/Common/LtsTimelineClassic/TimelineModel.cpp $
|
||||
* $Author: wille_m $
|
||||
* Created by Mike Wille, SLSE, 2014-10-01
|
||||
*/
|
||||
|
||||
#include "timeline_model.h"
|
||||
#include "timeline_view_drag_handler.h"
|
||||
#include "track.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace LtsTimeline
|
||||
{
|
||||
|
||||
TimelineModel::TimelineModel(TimelineView *view, QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_view(view)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.TimelineModel"))
|
||||
{
|
||||
m_rootTrack.m_id = nextFreeTrackId();
|
||||
m_trackCache.insert(std::pair<int, Track *>(m_rootTrack.m_id, &m_rootTrack));
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::clear()
|
||||
{
|
||||
m_rootTrack.m_childrenTracks.clear();
|
||||
m_rootTrack.m_items.clear();
|
||||
|
||||
//#warning // TODO: also clear copy item ids on re-summing!!
|
||||
m_rootTrack.m_copyItemIds.clear();
|
||||
m_trackCache.clear();
|
||||
//m_parentTrackCache.clear();
|
||||
m_itemCache.clear();
|
||||
|
||||
m_currentTrackId = 1; // skip root track (0)
|
||||
m_currentItemId = 0;
|
||||
m_trackCache.insert(std::pair<int, Track *>(m_rootTrack.m_id, &m_rootTrack)); // re-add root track
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::clearItems()
|
||||
{
|
||||
for (std::pair<int , Track *> p : m_trackCache)
|
||||
p.second->clearItems();
|
||||
|
||||
m_itemCache.clear();
|
||||
m_currentItemId = 0;
|
||||
}
|
||||
|
||||
|
||||
Track &TimelineModel::track(int id) noexcept(false)
|
||||
{
|
||||
if (!m_trackCaching)
|
||||
{
|
||||
if (id == m_rootTrack.id())
|
||||
return m_rootTrack;
|
||||
return m_rootTrack.track(id);
|
||||
}
|
||||
|
||||
auto it = m_trackCache.find(id);
|
||||
|
||||
if (it == m_trackCache.end())
|
||||
throw QStringLiteral("Model: track with id %1 not found in cache").arg(id);
|
||||
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
|
||||
const Track &TimelineModel::track(int id) const noexcept(false)
|
||||
{
|
||||
if (!m_trackCaching)
|
||||
{
|
||||
if (id == m_rootTrack.id())
|
||||
return m_rootTrack;
|
||||
return m_rootTrack.track(id);
|
||||
}
|
||||
|
||||
std::map<int /* trackId */, Track *>::const_iterator it = m_trackCache.find(id);
|
||||
|
||||
if (it == m_trackCache.cend())
|
||||
throw QStringLiteral("Model: const track with id %1 not found in cache").arg(id);
|
||||
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
|
||||
Item &TimelineModel::item(int id) noexcept(false)
|
||||
{
|
||||
if (!m_itemCaching)
|
||||
return m_rootTrack.item(id);
|
||||
|
||||
auto it = m_itemCache.find(id);
|
||||
|
||||
if (it == m_itemCache.end())
|
||||
throw QStringLiteral("Model: item with id %1 not found in cache").arg(id);
|
||||
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
|
||||
const Item &TimelineModel::item(int id) const noexcept(false)
|
||||
{
|
||||
if (!m_itemCaching)
|
||||
return m_rootTrack.item(id);
|
||||
|
||||
decltype(m_itemCache)::const_iterator it = m_itemCache.find(id);
|
||||
|
||||
if (it == m_itemCache.cend())
|
||||
throw QStringLiteral("Model: const item with id %1 not found in cache").arg(id);
|
||||
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
|
||||
const QVector<int> TimelineModel::itemIds() const
|
||||
{
|
||||
if (!m_itemCaching)
|
||||
return m_rootTrack.itemIds(true);
|
||||
|
||||
QVector<int> ids;
|
||||
std::transform(m_itemCache.cbegin(), m_itemCache.cend(), std::back_inserter(ids),
|
||||
[](const std::pair<int, Item *> &pair)
|
||||
{
|
||||
return pair.first;
|
||||
});
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
|
||||
//#warning // TODO: recursive doesn't make sense on some of the following item getters, as the root track will NEVER have items, so it MUST be always recursive!
|
||||
const QVector<int> TimelineModel::itemIdsBetween(qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted) const
|
||||
{
|
||||
return m_rootTrack.itemIdsBetween(start, end, strict, complete, recursive, sorted);
|
||||
}
|
||||
|
||||
bool TimelineModel::isItemsBetween(qint64 start, qint64 end, const QVector<int> &ignoreIds, bool strict, bool complete, bool recursive) const
|
||||
{
|
||||
return m_rootTrack.isItemsBetween(start, end, ignoreIds, strict, complete, recursive);
|
||||
}
|
||||
|
||||
const QVector<int> TimelineModel::copyItemIdsBetween(qint64 start, qint64 end, bool strict, bool complete, bool sorted) const
|
||||
{
|
||||
return m_rootTrack.copyItemIdsBetween(start, end, strict, complete, true, sorted);
|
||||
}
|
||||
|
||||
bool TimelineModel::isItemsBetween(qint64 start, qint64 end, int ignoreId, bool strict, bool complete, bool recursive) const
|
||||
{
|
||||
return m_rootTrack.isItemsBetween(start, end, ignoreId, strict, complete, recursive);
|
||||
}
|
||||
|
||||
|
||||
const QVector<int> TimelineModel::_copyItemIdsBetween(Track &t, qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const
|
||||
{
|
||||
QVector<int> itemIds;
|
||||
itemIds.reserve(t.itemCount());
|
||||
|
||||
// copy items
|
||||
for (const int itemId : qAsConst(t.copyItemIds()))
|
||||
{
|
||||
const Item &i = item(itemId);
|
||||
if (Track::isItemBetween(i, start, end, strict, complete, minimumDuration))
|
||||
itemIds += i.id();
|
||||
}
|
||||
|
||||
if (recursive)
|
||||
for (const TrackPtr &trackPtr : qAsConst(t.tracks()))
|
||||
itemIds += _copyItemIdsBetween(*trackPtr.get(), start, end, strict, complete, true, false, minimumDuration);
|
||||
|
||||
// remove duplicates because of copy items (TODO: is this even possible??)
|
||||
itemIds.erase(std::unique(itemIds.begin(), itemIds.end()), itemIds.end());
|
||||
|
||||
itemIds.squeeze(); // some items most likely have been filtered out, save memory
|
||||
|
||||
if (!sorted)
|
||||
return itemIds;
|
||||
|
||||
//#warning // TODO: insertion sort??
|
||||
std::sort(itemIds.begin(), itemIds.end(), [this](const int itemIdA, const int itemIdB)
|
||||
{
|
||||
try
|
||||
{
|
||||
return item(itemIdA).startTime() < item(itemIdB).startTime();
|
||||
}
|
||||
catch (...) { return false; } // hmm....
|
||||
});
|
||||
|
||||
return itemIds;
|
||||
}
|
||||
|
||||
|
||||
const QVector<int> TimelineModel::_itemIdsBetween(Track &t, qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const
|
||||
{
|
||||
QVector<int> itemIds;
|
||||
itemIds.reserve(t.itemCount());
|
||||
|
||||
for (const ItemPtr &itemPtr : qAsConst(t.items()))
|
||||
{
|
||||
const Item &i = *itemPtr;
|
||||
if (Track::isItemBetween(i, start, end, strict, complete, minimumDuration))
|
||||
itemIds += i.id();
|
||||
}
|
||||
|
||||
if (recursive)
|
||||
for (const TrackPtr &trackPtr : qAsConst(t.tracks()))
|
||||
itemIds += _itemIdsBetween(*trackPtr.get(), start, end, strict, complete, true, false, minimumDuration);
|
||||
|
||||
// remove duplicates because of copy items (TODO: is this even possible??)
|
||||
//itemIds.erase(std::unique(itemIds.begin(), itemIds.end()), itemIds.end());
|
||||
|
||||
itemIds.squeeze(); // some items most likely have been filtered out, save memory
|
||||
|
||||
if (!sorted)
|
||||
return itemIds;
|
||||
|
||||
//#warning // TODO: insertion sort??
|
||||
std::sort(itemIds.begin(), itemIds.end(), [this](const int itemIdA, const int itemIdB)
|
||||
{
|
||||
try
|
||||
{
|
||||
return item(itemIdA).startTime() < item(itemIdB).startTime();
|
||||
}
|
||||
catch (...) { return false; } // hmm....
|
||||
});
|
||||
|
||||
return itemIds;
|
||||
}
|
||||
|
||||
|
||||
bool TimelineModel::_isItemsBetween(const Track &t, qint64 start, qint64 end, const QVector<int> &ignoreIds, bool strict, bool complete, bool recursive, qint64 minimumDuration) const
|
||||
{
|
||||
for (const ItemPtr &itemPtr : qAsConst(t.items()))
|
||||
{
|
||||
const Item &i = *itemPtr;
|
||||
if (!ignoreIds.contains(i.id()))
|
||||
if (Track::isItemBetween(i, start, end, strict, complete, minimumDuration))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!recursive)
|
||||
return false;
|
||||
|
||||
for (const TrackPtr &trackPtr : qAsConst(t.tracks()))
|
||||
if (_isItemsBetween(*trackPtr.get(), start, end, ignoreIds, strict, complete, true, minimumDuration))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool TimelineModel::_isItemsBetween(const Track &t, qint64 start, qint64 end, int ignoreId, bool strict, bool complete, bool recursive, qint64 minimumDuration) const
|
||||
{
|
||||
return _isItemsBetween(t, start, end, QVector<int>({ ignoreId }), strict, complete, recursive, minimumDuration);
|
||||
}
|
||||
|
||||
|
||||
QVariant TimelineModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if ((role != Qt::DisplayRole && role != Qt::UserRole) || !index.isValid())
|
||||
return {};
|
||||
|
||||
int trackId = index.internalId();
|
||||
int column = index.column();
|
||||
|
||||
if (column == 1 && role == Qt::DisplayRole) // item column (1)
|
||||
return trackId;
|
||||
|
||||
if (role == Qt::UserRole) // doesn't matter what column, as a track is a row ;)
|
||||
{
|
||||
if (column == 0)
|
||||
return trackId; // ffs
|
||||
|
||||
try
|
||||
{
|
||||
const Track &t = track(trackId);
|
||||
return t.userData();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (column != 0) // actuator column (0) AND display role! (can only be display role and user role - but user role would have returned already)
|
||||
return {};
|
||||
|
||||
|
||||
QString text;
|
||||
|
||||
try
|
||||
{
|
||||
const Track &t = track(trackId);
|
||||
|
||||
#if 0 // #ifdef LTS_DEBUG
|
||||
text = QStringLiteral("[%1] %3").arg(t.id()).arg(t.text());
|
||||
#else
|
||||
text = t.text();
|
||||
#endif
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return QStringLiteral("[?????]");
|
||||
}
|
||||
|
||||
if (text.isEmpty())
|
||||
text = tr("[unknown]");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
Qt::ItemFlags TimelineModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
return QAbstractItemModel::flags(index);
|
||||
}
|
||||
|
||||
|
||||
QModelIndex TimelineModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent))
|
||||
return {};
|
||||
|
||||
int childTrackId;
|
||||
try
|
||||
{
|
||||
const Track &parentTrack = parent.isValid() ? track(parent.internalId()) : m_rootTrack;
|
||||
const Track &childTrack = parentTrack.trackAt(row); // throws - qt!
|
||||
childTrackId = /*static_cast<quintptr>(*/childTrack.id()/*)*/;
|
||||
}
|
||||
catch (const QString &msg)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << Q_FUNC_INFO << "cannot get parent track " << msg;
|
||||
return {};
|
||||
}
|
||||
catch (const std::out_of_range &oor)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << Q_FUNC_INFO << "cannot get child track " << QString::fromUtf8(oor.what());
|
||||
return {};
|
||||
}
|
||||
|
||||
return createIndex(row, column, childTrackId);
|
||||
}
|
||||
|
||||
|
||||
int TimelineModel::rowOfTrackId(int id) const noexcept(false)
|
||||
{
|
||||
const Track &parent = track/*2*/(track/*2*/(id).parentId()); // throws!
|
||||
|
||||
if (parent.items().empty())
|
||||
throw("parent doesn't have items");
|
||||
|
||||
int row = 0;
|
||||
|
||||
for (const TrackPtr &trackPtr : qAsConst(parent.tracks()))
|
||||
{
|
||||
if (trackPtr->id() == id)
|
||||
return row;
|
||||
row++;
|
||||
}
|
||||
|
||||
throw("not found");
|
||||
}
|
||||
|
||||
|
||||
const Track &TimelineModel::trackOfIndex(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
throw QStringLiteral("trackOfIndex: index is invalid!");
|
||||
|
||||
// get child track
|
||||
try
|
||||
{
|
||||
return track(index.internalId());
|
||||
}
|
||||
catch (const QString &msg)
|
||||
{
|
||||
throw QStringLiteral("trackOfIndex: cannot find track by internal id %1: %2").arg(index.internalId()).arg(msg);
|
||||
}
|
||||
|
||||
// TODO: assert? or is this okay-ish behaviour?
|
||||
return m_rootTrack;
|
||||
}
|
||||
|
||||
|
||||
//void TimelineModel::move(int fromTrackId, int itemId, int toTrackId, qint64 newStartTime) noexcept(false)
|
||||
//{
|
||||
// // NOTE: no need to modify cache, as the item class / ptr doesn't change
|
||||
// Track &sourceTrack = track(fromTrackId); // throws!
|
||||
|
||||
// ItemPtr itemPtr = sourceTrack.takeItem(itemId); // throws!
|
||||
// itemPtr->_setStartTime(newStartTime);
|
||||
|
||||
// Track &destTrack = track(toTrackId); // throws!
|
||||
// destTrack.addItemPtr(std::move(itemPtr)); // sorts!
|
||||
|
||||
//#warning // TODO: !
|
||||
// emit dataChanged({},{});
|
||||
//}
|
||||
|
||||
|
||||
const Track &TimelineModel::parentOfTrackId(int id) const noexcept(false)
|
||||
{
|
||||
if (!m_trackCaching)
|
||||
{
|
||||
if (m_rootTrack.hasTrack(id))
|
||||
return m_rootTrack;
|
||||
// return m_rootTrack.parentOf(id);
|
||||
//not found look the old way instead
|
||||
}
|
||||
|
||||
const Track &theTrack = track(id);
|
||||
|
||||
if (theTrack.parentId() == m_rootTrack.id())
|
||||
return m_rootTrack;
|
||||
|
||||
return track(theTrack.parentId());
|
||||
}
|
||||
|
||||
|
||||
QModelIndex TimelineModel::parent(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
// => gib mir den parent track vom dem gegebenen track
|
||||
|
||||
/*
|
||||
[ root ] <= auf dem parent des parent muss also geschaut werden, in welcher row der parent liegt!
|
||||
[ sub ] <= der parent!
|
||||
[ sub ] <= gib mir den parent von dem da
|
||||
*/
|
||||
|
||||
// createIndex mit ...:
|
||||
// die row, in dem der parent liegt
|
||||
// die column ... 0
|
||||
// die track id from parent
|
||||
|
||||
// man will also die ROW vom parent, indem man auf dem parent des parent guckt, in welcher row er liegt
|
||||
|
||||
int trackId = index.internalId();
|
||||
int parentId;
|
||||
int superParentId;
|
||||
try
|
||||
{
|
||||
const Track &parent = parentOfTrackId(trackId);
|
||||
parentId = parent.id();
|
||||
superParentId = parent.parentId();
|
||||
}
|
||||
catch (const QString &msg)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << Q_FUNC_INFO << "cannot get parent of track with id" << trackId << msg;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (parentId == m_rootTrack.id()) // there is no parent for the parent
|
||||
return {};
|
||||
|
||||
int parentsRow;
|
||||
try
|
||||
{
|
||||
const Track &superParent = track(superParentId);
|
||||
|
||||
try
|
||||
{
|
||||
parentsRow = superParent.rowOfTrack(parentId);
|
||||
}
|
||||
catch (const QString &msg)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << Q_FUNC_INFO << "row of track for parent not found for parent id" << parentId << "-" << msg;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
catch (const QString &msg)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << Q_FUNC_INFO << "super parent not found for super parent id" << superParentId << "-" << msg;
|
||||
return {};
|
||||
}
|
||||
|
||||
return createIndex(parentsRow, 0, parentId);
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::setItemStartTime(Track &t, Item &i, qint64 newStart)
|
||||
{
|
||||
i._setStartTime(newStart);
|
||||
t.sort(); // TODO: or just invalidate?
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::setItemStartTime(Item &i, qint64 newStart) noexcept(false)
|
||||
{
|
||||
setItemStartTime(track(i.parentTrackId()), i, newStart);
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::setItemStartTime(int itemId, qint64 newStart) noexcept(false)
|
||||
{
|
||||
setItemStartTime(item(itemId), newStart);
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::setItemDuration(Track &t, Item &i, int duration)
|
||||
{
|
||||
i._setDuration(std::max(duration, m_minItemDuration));
|
||||
t.sort(); // TODO: or just invalidate?
|
||||
}
|
||||
|
||||
void TimelineModel::setItemDuration(Item &i, int duration) noexcept(false)
|
||||
{
|
||||
setItemDuration(track(i.parentTrackId()), i, duration);
|
||||
}
|
||||
|
||||
void TimelineModel::setItemDuration(int itemId, int duration) noexcept(false)
|
||||
{
|
||||
setItemDuration(item(itemId), duration);
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::copyToClipboard(const QVector<int> &ids) noexcept(false)
|
||||
{
|
||||
m_clipboard.clear();
|
||||
m_clipboard.reserve(ids.size());
|
||||
qint64 tStartMin = LLONG_MAX;
|
||||
for (int itemId : qAsConst(ids))
|
||||
{
|
||||
const Item &i = item(itemId);
|
||||
ItemPtr iPtr = std::make_unique<Item>(Item(i.startTime(), i.duration(), i.color()));
|
||||
iPtr->m_parentTrackId = i.m_parentTrackId;
|
||||
m_clipboard.push_back(std::move(iPtr));
|
||||
tStartMin = std::min(tStartMin, i.startTime());
|
||||
}
|
||||
for (ItemPtr &iPtr : m_clipboard)
|
||||
iPtr->_setStartTime(iPtr->startTime() - tStartMin);
|
||||
}
|
||||
|
||||
void TimelineModel::cutToClipboard(const QVector<int> &ids) noexcept(false)
|
||||
{
|
||||
copyToClipboard(ids);
|
||||
for (int itemId : qAsConst(ids))
|
||||
removeItem(itemId);
|
||||
}
|
||||
|
||||
void TimelineModel::pasteFromClipboard(qint64 time) noexcept(false)
|
||||
{
|
||||
if (m_isSnapEnabled)
|
||||
time -= (time % m_snapStep);
|
||||
for (ItemPtr &iPtr : m_clipboard)
|
||||
{
|
||||
iPtr->_setStartTime(time + iPtr->startTime());
|
||||
addItem(iPtr->parentTrackId(), std::move(iPtr));
|
||||
}
|
||||
m_clipboard.clear();
|
||||
}
|
||||
|
||||
|
||||
int TimelineModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
if (!parent.isValid())
|
||||
return m_rootTrack.tracks().size();
|
||||
|
||||
try
|
||||
{
|
||||
return track(parent.internalId()).tracks().size();
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
QModelIndex TimelineModel::indexOfTrack(int trackId) const
|
||||
{
|
||||
for (int row = 0; row < rowCount(); ++row)
|
||||
{
|
||||
QModelIndex idx = indexOfTrackFromIndex(index(row, 0, {}), trackId);
|
||||
if (idx.isValid())
|
||||
return idx;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
QModelIndex TimelineModel::indexOfTrackFromIndex(const QModelIndex &idx, int trackId) const
|
||||
{
|
||||
if (static_cast<int>(idx.internalId()) == trackId)
|
||||
return index(idx.row(), 1, idx.parent());
|
||||
|
||||
for (int row = 0; row < rowCount(idx); ++row)
|
||||
{
|
||||
const QModelIndex i = indexOfTrackFromIndex(index(row, 0, idx), trackId);
|
||||
if (i.isValid())
|
||||
return i;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
QModelIndex TimelineModel::indexOfTrackSafe(int trackId) const noexcept(false)
|
||||
{
|
||||
for (int row = 0; row < rowCount(); ++row)
|
||||
{
|
||||
const QModelIndex idx(indexOfTrackFromIndex(index(row, 0 /* actuator */, {}), trackId));
|
||||
if (idx.isValid())
|
||||
return idx;
|
||||
}
|
||||
|
||||
QString trackText;
|
||||
try
|
||||
{
|
||||
trackText = track(trackId).text();
|
||||
} catch(...){}
|
||||
|
||||
throw QStringLiteral("indexOfTrackSafe: index for trackId %1 ('%2') not found ").arg(trackId).arg(trackText.isEmpty() ? QStringLiteral("(unknown)") : trackText);
|
||||
}
|
||||
|
||||
|
||||
QModelIndex TimelineModel::indexOfTrackFromIndexSafe(const QModelIndex &idx, int trackId) const noexcept(false)
|
||||
{
|
||||
if (static_cast<int>(idx.internalId()) == trackId)
|
||||
return idx;
|
||||
|
||||
for (int row = 0; row < rowCount(idx); ++row)
|
||||
{
|
||||
const QModelIndex i = indexOfTrackFromIndex(index(row, 1, idx), trackId);
|
||||
if (i.isValid())
|
||||
return i;
|
||||
}
|
||||
|
||||
throw QStringLiteral("indexOfTrackFromIndexSafe: index for trackId %1 not found").arg(trackId);
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::moveItemToTrack(int itemId, int trackId, qint64 time) noexcept(false)
|
||||
{
|
||||
/* NOTE: CAUTION! There is no rollback functionality - so be sure to only call
|
||||
* safe, non-throwing functions that are guaranteed to succeed after the item
|
||||
* was stolen, the new start time was set or the new parent id was set.
|
||||
* Otherwise the result will be undefined and have inconsistent data. */
|
||||
|
||||
const Item &theItem = item(itemId);
|
||||
Track &oldTrack = track(theItem.parentTrackId());
|
||||
Track &newTrack = track(trackId);
|
||||
|
||||
ItemPtr itemPtr = oldTrack.takeItem(itemId); // stolen!
|
||||
// point of no return (see comment!)
|
||||
Item *theItemPtr = newTrack.addItem(std::move(itemPtr)); // doesn't throw!
|
||||
setItemStartTime(newTrack, *theItemPtr, time); // doesn't throw!
|
||||
theItemPtr->m_parentTrackId = trackId; // set item's parent id to new track's id
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::removeItem(int itemId) noexcept(false)
|
||||
{
|
||||
const Item &i = item(itemId);
|
||||
Track &t = track(i.parentTrackId());
|
||||
|
||||
t.removeItem(itemId);
|
||||
|
||||
auto it = m_itemCache.find(itemId);
|
||||
if (it == m_itemCache.end())
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
throw QStringLiteral("removeItem: item with id %1 not found").arg(itemId);
|
||||
}
|
||||
|
||||
m_itemCache.erase(it);
|
||||
|
||||
// QModelIndex idx = indexOfTrack(t.id());
|
||||
// emit dataChanged(idx, idx);
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::removeTrack(int trackId) noexcept(false)
|
||||
{
|
||||
if (trackId == m_rootTrack.id())
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
throw QStringLiteral("removeTrack: the root track shouldn't and *can't* be deleted");
|
||||
}
|
||||
|
||||
// first *all* items of the track *and its sub-tracks* have to be erased from this model's item cache
|
||||
const Track &t = track(trackId);
|
||||
Track &parentTrack = track(t.parentId());
|
||||
|
||||
for (int itemId : t.itemIds(true))
|
||||
{
|
||||
auto it = m_itemCache.find(itemId);
|
||||
|
||||
if (it == m_itemCache.end())
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
m_itemCache.erase(it);
|
||||
}
|
||||
|
||||
// get the parent of the Track, to tell it to remove its child (the given track id) NOTE: lowest level of parent track is m_rootTrack
|
||||
parentTrack.removeTrack(trackId);
|
||||
|
||||
auto it = m_trackCache.find(trackId);
|
||||
if (it == m_trackCache.end())
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_trackCache.erase(it);
|
||||
}
|
||||
|
||||
|
||||
void TimelineModel::setMinimumDuration(int duration)
|
||||
{
|
||||
if (duration < 0)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "negative duration is not possible -> setting minimum duration to 0";
|
||||
duration = 0;
|
||||
}
|
||||
if (duration % 1000 && m_isSnapEnabled)
|
||||
WARNING_LOG(m_logger) << "Snapping to 1000ms enabled, yet minimum duration is not a multiple of 1000ms: " << duration;
|
||||
m_minItemDuration = duration;
|
||||
}
|
||||
|
||||
bool TimelineModel::canDropItem(const Item &item, int trackId, qint64 start) const
|
||||
{
|
||||
Q_UNUSED(item);
|
||||
Q_UNUSED(trackId);
|
||||
Q_UNUSED(start);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimelineModel::canMoveItem(const Item &item, qint64 start) const
|
||||
{
|
||||
Q_UNUSED(item);
|
||||
Q_UNUSED(start);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimelineModel::canResizeItem(const Item &item, qint64 start, int duration) const
|
||||
{
|
||||
Q_UNUSED(item);
|
||||
Q_UNUSED(start);
|
||||
Q_UNUSED(duration);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool TimelineModel::isItemDragged(int _itemId) const
|
||||
{
|
||||
return m_view->dragHandler() ? m_view->dragHandler()->dragItemIds().contains(_itemId) : false;
|
||||
}
|
||||
|
||||
} // namespace LtsTimeline
|
||||
Reference in new issue
Block a user