/** * @RDE_HEADER@ * manages & represents a row in view and contains children Tracks & events * $Id: Track.cpp 51204 2020-05-20 15:47:37Z wille_m $ * $HeadURL: svn://192.168.133.21/TDS/branches/product/features/hotline/Lts/Common/LtsEventTimeline/Track.cpp $ * $Author: wille_m $ * Created by Mike Wille, SLSE, 2014-10-01 */ #include "track.h" namespace LtsTimeline { const Track &Track::track(int id) const noexcept(false) { // find track inside this track (aka. non-recursive) auto const it = std::find_if(m_childrenTracks.cbegin(), m_childrenTracks.cend(), [id](const TrackPtr &trackPtr) { return (trackPtr.get()->id() == id); }); if (it != m_childrenTracks.cend()) return **it; // find track in children tracks (recursive) for (const TrackPtr &track : qAsConst(m_childrenTracks)) { try { return track->track(id); } catch(...) { /* exception just means the specified track was not found on that track * that means that either the item is on another track * or that the track really doesn't exists, that's where the throw some * lines ahead comes into play */ } } throw QStringLiteral("lookup failed!");; } bool Track::hasTrack(int id, bool recursive) const { if (std::find_if(m_childrenTracks.cbegin(), m_childrenTracks.cend(), [id](const TrackPtr &trackPtr) { return (trackPtr.get()->id() == id); }) != m_childrenTracks.cend()) return true; if (!recursive) return false; for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) { if (trackPtr->hasTrack(id, true)) return true; } return false; } //int Track::trackIdOfRow(int row) const noexcept(false) //{ // // look inside this track ... // try // { // return trackAt(row).id(); // } // catch(...) // { //// // ... otherwise in children //// for (const std::unique_ptr &trackPtr : qAsConst(m_childrenTracks)) //// { //// try //// { //// return trackPtr->trackIdOfRow(idx.row()); //// } catch(...) {} //// } // } // throw("not found"); //} void Track::addCopyItemIds(const QSet &ids) { m_copyItemIds += ids; } void Track::addCopyItemId(int id) { m_copyItemIds += id; } const Track &Track::parentOf(int id) const noexcept(false) { for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) { if (trackPtr->hasTrack(id)) // not recursive! return *trackPtr; if (trackPtr->hasTrack(id, true)) return trackPtr->parentOf(id); } throw("not found"); } bool Track::isItemBetween(const Item &item, qint64 start, qint64 end, bool strict, bool complete, qint64 minimumDuration) { if (end - start == -1) { if (item.duration() == -1) { if (!complete) return true; return strict ? item.startTime() > start : item.startTime() >= start; } if (strict) { return (complete && item.startTime() > start) || (!complete && item.startTime() + item.duration() > start); } else { return (complete && item.startTime() >= start) || (!complete && item.startTime() + item.duration() >= start); } } if (item.duration() == -1) { if (complete) return false; return strict ? item.startTime() < end : item.startTime() <= end; } Q_ASSERT(end+1 /* there can be events that would have exactly 0 ms in width, so let end == start not assert! */ > start); qint64 itemStart = item.startTime(), itemEnd = item.startTime() + (std::max((qint64)item.duration(), minimumDuration)); //fprintf(stderr, ">>>>>>>>>> between: %lld and %lld\n", itemStart, itemEnd); if (strict) return ((complete && (itemStart > start && itemEnd < end)) || (!complete && (itemEnd > start && itemStart < end))); else return ((complete && (itemStart >= start && itemEnd <= end)) || (!complete && (itemEnd >= start && itemStart <= end))); } void Track::removeCopyItem(int itemId) noexcept(false) { m_copyItemIds.remove(itemId); for (TrackPtr &t : m_childrenTracks) { t->removeCopyItem(itemId); } } const QVector Track::copyItemIdsBetween(qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const { auto itemPtrRefs = _copyItems(); //fprintf(stderr, ">>>>>>>>>> b4: %lu - s: %lld - e: %lld\n", itemPtrRefs.size(), start, end); if (recursive) { for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) { auto itemPtrRefs2 = trackPtr->_copyItems(); itemPtrRefs.insert(itemPtrRefs.end(), itemPtrRefs2.begin(), itemPtrRefs2.end()); } } itemPtrRefs.erase(std::remove_if(itemPtrRefs.begin(), itemPtrRefs.end(), [this, start, end, strict, complete](const ItemPtr &itemPtr) { return !isItemBetween(*itemPtr, start, end, strict, complete); }), itemPtrRefs.end()); if (sorted) { sort(itemPtrRefs); } //fprintf(stderr, ">>>>>>>>>> %lu\n", itemPtrRefs.size()); return transformItemPtrsToIds(itemPtrRefs); } const QVector Track::itemIdsBetween(qint64 start, qint64 end, bool strict, bool complete, bool recursive, bool sorted, qint64 minimumDuration) const { if (sorted) { auto itemPtrRefs = _items(recursive); itemPtrRefs.erase(std::remove_if(itemPtrRefs.begin(), itemPtrRefs.end(), [this, start, end, strict, complete, minimumDuration](const ItemPtr &itemPtr) { return !isItemBetween(*itemPtr, start, end, strict, complete, minimumDuration); }), itemPtrRefs.end()); sort(itemPtrRefs); return transformItemPtrsToIds(itemPtrRefs); } // unsorted QVector itemIds; itemIds.reserve(m_items.size()); for (const ItemPtr &itemPtr : qAsConst(m_items)) { const Item &item = *itemPtr; if (isItemBetween(item, start, end, strict, complete, minimumDuration)) itemIds += item.id(); } if (!recursive) { itemIds.squeeze(); // reserved space that was not used because some items were filtered-out by isItemBetween(..) return itemIds; } for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) itemIds += trackPtr->itemIdsBetween(start, end, strict, complete, true, minimumDuration); return itemIds; } bool Track::isItemsBetween(qint64 start, qint64 end, const QVector &ignoreIds, bool strict, bool complete, bool recursive, qint64 minimumDuration) const { for (const ItemPtr &itemPtr : qAsConst(m_items)) { const Item &i = *itemPtr; if (!ignoreIds.contains(i.id())) if (isItemBetween(i, start, end, strict, complete, minimumDuration)) return true; } // also check copy items for (const int itemId : qAsConst(m_copyItemIds)) { const Item &i = item(itemId); if (!ignoreIds.contains(i.id())) if (isItemBetween(i, start, end, strict, complete, minimumDuration)) return true; } if (!recursive) return false; for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) if (trackPtr->isItemsBetween(start, end, ignoreIds, strict, complete, true, minimumDuration)) return true; return false; } bool Track::isItemsBetween(qint64 start, qint64 end, int ignoreId, bool strict, bool complete, bool recursive, qint64 minimumDuration) const { return isItemsBetween(start, end, QVector({ ignoreId }), strict, complete, recursive, minimumDuration); } ItemPtrRefs Track::_items(bool recursive) const // private! { // this items ItemPtrRefs x(m_items.cbegin(), m_items.cend()); if (!m_copyItemIds.isEmpty()) x.reserve(x.size() + m_copyItemIds.count()); // copy items for (const int itemId : qAsConst(m_copyItemIds)) { try { x.push_back(std::cref(_item(itemId))); } catch (const QString &msg) { fprintf(stderr, "cannot add item ptr ref from copy item with copy item id %d: %s\n", itemId, msg.toStdString().c_str()); } } if (!recursive) return x; // children's items - if recursive for (const TrackPtr &t : qAsConst(m_childrenTracks)) { const ItemPtrRefs x2(t->_items(true)); x.insert(x.end(), x2.begin(), x2.end()); //std::copy(x2.cbegin(), x2.cend(), std::back_inserter(x)); } return x; } ItemPtrRefs Track::_copyItems() const // private, non-recursive, unsorted { // this items ItemPtrRefs x; for (int id : qAsConst(m_copyItemIds)) { try { x.push_back(_item(id)); } catch(...) { fprintf(stderr, "Track::_copyItems: cannot find item for item id %d\n", id); continue; } } return x; } ItemPtrNCVRefs Track::_itemsNCV(bool recursive) // private! { // this items ItemPtrNCVRefs x(m_items.begin(), m_items.end()); if (!recursive) return x; // children's items - if recursive for (TrackPtr &t : m_childrenTracks) { ItemPtrNCVRefs x2(t->_itemsNCV(true)); x.insert(x.end(), x2.begin(), x2.end()); //std::copy(x2.cbegin(), x2.cend(), std::back_inserter(x)); } return x; } const QVector Track::_itemIds(bool recursive) const // unsorted, private! { QVector itemIds(transformItemPtrsToIds(m_items)); // this track's items if (!recursive) return itemIds; // children track's items for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) itemIds += trackPtr->_itemIds(true); return itemIds; } ItemPtrRef Track::_item(int id) const noexcept(false) { // find item inside this track (aka. non-recursive) auto const it = std::find_if(m_items.cbegin(), m_items.cend(), [id](const ItemPtr &itemPtr) { return (itemPtr.get()->id() == id); }); if (it != m_items.cend()) return *it; // find item in children tracks (recursive) for (const TrackPtr &track : qAsConst(m_childrenTracks)) { try { return track->_item(id); } catch(...) { /* exception just means the specified item was not found on that track * that means that either the item is on another track * or that the item really doesn't exists, that's where the throw some * lines ahead comes into play */ } } throw QStringLiteral("_item: item with id %1 not found!").arg(id); } void Track::printTrackIds(bool withItems, int level) const { fprintf(stderr, "%*c Track %16.16s ", level * 2, '>', text().toStdString().c_str()); if (withItems) { fprintf(stderr, " ( "); for (const ItemPtr &itemPtr : qAsConst(m_items)) { fprintf(stderr, "%d ", itemPtr->id()); } fprintf(stderr, ")"); } fprintf(stderr, "\n"); for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) trackPtr->printTrackIds(level + 1); } QVector Track::itemIds(bool recursive, bool sorted) const { if (sorted) { if (!recursive) // there is only a need to sort, if the call is recursive, because all items are already sorted inside a track return transformItemPtrsToIds(m_items); // sorted & recursive ItemPtrRefs is(_items(true)); sort(is); // need to re-sort, as items are now mixed up by appended items return transformItemPtrsToIds(is); } // unsorted QVector itemIds; itemIds.reserve(m_items.size()); // reserves only for this track's items, as we only know how big the vector has to be if we're not doing recursive lookup itemIds += transformItemPtrsToIds(m_items); if (!recursive) return itemIds; // recursive, at this point for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) itemIds += trackPtr->_itemIds(true); return itemIds; } void Track::addItemPtr(ItemPtr &&itemPtr) { m_items.insert(std::upper_bound(m_items.begin(), m_items.end(), itemPtr->startTime(), [](qint64 startTime, const ItemPtr &other) { return (other->startTime() > startTime); }), std::move(itemPtr)); } void Track::removeItem(int itemId) noexcept(false) { auto it = std::find_if(m_items.begin(), m_items.end(), [itemId](const ItemPtr &itemPtr) { return (itemPtr->id() == itemId); }); if (it == m_items.end()) throw QStringLiteral("removeItem: item %1 not found").arg(itemId); m_items.erase(it); } void Track::removeTrack(int trackId) noexcept(false) // non-recursive! { auto it = std::find_if(m_childrenTracks.begin(), m_childrenTracks.end(), [trackId](const TrackPtr &trackPtr) { return (trackPtr->id() == trackId); }); if (it == m_childrenTracks.end()) throw QStringLiteral("removeTrack: track %1 not found on track %2").arg(trackId, id()); m_childrenTracks.erase(it); } ItemPtr Track::takeItem(int itemId) noexcept(false) { auto it = std::find_if(m_items.begin(), m_items.end(), [itemId](const ItemPtr &itemPtr) { return (itemPtr->id() == itemId); }); if (it == m_items.end()) throw QStringLiteral("Track::takeItem: item with id %1 not found").arg(itemId); ItemPtr ptr = std::move(*it); // NOTE: mustn't throw after this point! m_items.erase(it); return ptr; } //bool Track::sumEvents() //{ // // first sum the children's tracks (recursive!) (if any) ... // for (auto child : m_childrenTracks) // child->sumEvents(); // // ... then sum our own // // get all events per tick from all children ... // ItemPtrRefs itemPtrRefs; // for (auto child : qAsConst(m_childrenTracks)) // { // auto childItemPtrRefs = _items(false /* not recursive! as the items on the children are already sum'd */); // itemPtrRefs.insert(itemPtrRefs.end(), childItemPtrRefs.begin(), childItemPtrRefs.end()); // } // /* ... then re-create the events or // * put them into Block Items if they collide OR if the're already Block Items */ //} Track &Track::track(int id) noexcept(false) { // find track inside this track (aka. non-recursive) auto it = std::find_if(m_childrenTracks.begin(), m_childrenTracks.end(), [id](TrackPtr &trackPtr) { return (trackPtr.get()->id() == id); }); if (it != m_childrenTracks.end()) { Track &t = **it; //fprintf(stderr, ">> !!! track(): got %6d from %6d\n", t.id(), this->id()); return t; } // find track in children tracks (recursive) for (TrackPtr &track : m_childrenTracks) { try { return track->track(id); } catch(...) { /* exception just means the specified track was not found on that track * that means that either the item is on another track * or that the track really doesn't exists, that's where the throw some * lines ahead comes into play */ } } throw QStringLiteral("Track: track with id %1 not found").arg(id); } ItemPtrNCVRef Track::_itemNCV(int id) noexcept(false) { // find item inside this track (aka. non-recursive) auto const it = std::find_if(m_items.begin(), m_items.end(), [id](ItemPtr &itemPtr) { return (itemPtr.get()->id() == id); }); if (it != m_items.end()) return *it; // find item in children tracks (recursive) for (TrackPtr &track : m_childrenTracks) { try { return track->_itemNCV(id); } catch(...) { /* exception just means the specified item was not found on that track * that means that either the item is on another track * or that the item really doesn't exists, that's where the throw some * lines ahead comes into play */ } } throw QStringLiteral("lookup failed!");; } Item &Track::item(int id) noexcept(false) // recursive! { // find item inside this track (aka. non-recursive) auto const it = std::find_if(m_items.begin(), m_items.end(), [id](ItemPtr &itemPtr) { return (itemPtr.get()->id() == id); }); if (it != m_items.end()) return **it; // find item in children tracks (recursive) for (TrackPtr &track : m_childrenTracks) { try { return track->item(id); } catch(...) { /* exception just means the specified item was not found on that track * that means that either the item is on another track * or that the item really doesn't exists, that's where the throw some * lines ahead comes into play */ } } throw QStringLiteral("lookup failed!");; } const Item &Track::item(int id) const noexcept(false) // recursive! { // find item inside this track (aka. non-recursive) auto const it = std::find_if(m_items.cbegin(), m_items.cend(), [id](const ItemPtr &itemPtr) { return (itemPtr.get()->id() == id); }); if (it != m_items.cend()) return **it; // find item in children tracks (recursive) for (const TrackPtr &track : qAsConst(m_childrenTracks)) { try { return track->item(id); } catch(...) { /* exception just means the specified item was not found on that track * that means that either the item is on another track * or that the item really doesn't exists, that's where the throw some * lines ahead comes into play */ } } throw QStringLiteral("lookup failed!");; } //bool Track::previous(int id, Item &item_out) noexcept(false) //{ // // find item inside this track (aka. non-recursive) // auto it = std::find_if(m_items.begin(), m_items.end(), [id](std::unique_ptr &itemPtr) // { // return (itemPtr.get()->id() == id); // }); // if (it != m_items.end() && (*it) != m_items.front()) // { // item_out = **(--it); // return true; // } // return false; //} //bool Track::next(int id, Item &item_out) noexcept(false) //{ // // find item inside this track (aka. non-recursive) // auto it = std::find_if(m_items.begin(), m_items.end(), [id](std::unique_ptr &itemPtr) // { // return (itemPtr.get()->id() == id); // }); // if (it != m_items.end() && (*it) != m_items.back()) // { // item_out = **(++it); // return true; // } // return false; //} //bool Track::itemAt(qint64 time, Item &item_out) noexcept(false) //{ // // find item inside this track (aka. non-recursive) // auto const& it = std::find_if(m_items.begin(), m_items.end(), [time](std::unique_ptr &itemPtr) // { // return itemPtr.get()->startTime() <= time && time <= itemPtr.get()->startTime() + itemPtr.get()->duration(); // }); // if (it != m_items.end()) // { // item_out = **it; // return true; // } // return false; //} Item &Track::previous(int id) noexcept(false) { // find item inside this track (aka. non-recursive) auto it = std::find_if(m_items.begin(), m_items.end(), [id](ItemPtr &itemPtr) { return (itemPtr.get()->id() == id); }); if (it != m_items.end() && (*it) != m_items.front()) return **(--it); throw QStringLiteral("Track::previous: previous item for id %1 not found").arg(id); } Item &Track::next(int id) noexcept(false) { // find item inside this track (aka. non-recursive) auto it = std::find_if(m_items.begin(), m_items.end(), [id](ItemPtr &itemPtr) { return (itemPtr.get()->id() == id); }); if (it != m_items.end() && (*it) != m_items.back()) return **(++it); throw QStringLiteral("lookup failed!");; } //bool Track::itemAt(qint64 time, Item &item_out) noexcept(false) //{ // // find item inside this track (aka. non-recursive) // auto const& it = std::find_if(m_items.begin(), m_items.end(), [time](std::unique_ptr &itemPtr) // { // return itemPtr.get()->startTime() <= time && time <= itemPtr.get()->startTime() + itemPtr.get()->duration(); // }); // if (it != m_items.end()) // { // item_out = **it; // return true; // } // return false; //} int Track::rowOfTrack(int id) const noexcept(false) { int row = 0; for (const TrackPtr &trackPtr : qAsConst(m_childrenTracks)) { if (trackPtr->id() == id) return row; row++; } throw("not found"); } Track &Track::trackAt(int index) noexcept(false) { return *m_childrenTracks.at(index); } const Track &Track::trackAt(int index) const noexcept(false) { return *m_childrenTracks.at(index); } const Item &Track::copyItemAt(qint64 time) const noexcept(false) { auto const itemPtrRefs = _copyItems(); const ItemPtrRefs::const_iterator it = std::find_if(itemPtrRefs.cbegin(), itemPtrRefs.cend(), [time](const ItemPtr &itemPtr) { return itemPtr.get()->startTime() <= time && time <= itemPtr.get()->startTime() + itemPtr.get()->duration(); }); if (it != itemPtrRefs.cend()) return *it->get(); throw QStringLiteral("Track: can't find copy item at %1").arg(time); } Item &Track::itemAt(qint64 time) noexcept(false) { // find item inside this track (aka. non-recursive) auto it = std::find_if(m_items.cbegin(), m_items.cend(), [time](const ItemPtr &itemPtr) { return itemPtr.get()->startTime() <= time && time <= itemPtr.get()->startTime() + itemPtr.get()->duration(); }); if (it != m_items.cend()) return **it; throw QStringLiteral("lookup failed!");; // TODO: un-mep } const Item &Track::itemAt(qint64 time) const noexcept(false) { // find item inside this track (aka. non-recursive) auto const it = std::find_if(m_items.cbegin(), m_items.cend(), [time](const ItemPtr &itemPtr) { return itemPtr.get()->startTime() <= time && time <= itemPtr.get()->startTime() + itemPtr.get()->duration(); }); if (it != m_items.cend()) return **it; throw QStringLiteral("lookup failed!");; // TODO: un-mep } } // namespace LtsTimeline //