/** * @RDE_HEADER@ * manages & represents a row in view and contains children items & events * $Id$ * $HeadURL$ * $Author$ * Created by Mike Wille, SLSE, 2014-10-01 */ #include #include "Item.h" #include "EventTools.h" #include "EventGraphic.h" #ifdef LTS_DEBUG //#define VERBOSE #endif namespace LtsTimelineClassic { Item::Item(const QString &name, const QString &identifier) : m_name(name) , m_identifier(identifier) , m_children(nullptr) , m_parent(nullptr) , m_pevcon(nullptr) , m_eventListChanged(true) // true, as a NEW list is always changed { } Item::~Item() { delete m_pevcon; if (m_children) { qDeleteAll(*m_children); delete m_children; } qDeleteAll(m_eventList); } PassiveEventContainer *Item::getPevcon() { if (!m_pevcon) // create a new pevcon if there is no pevcon m_pevcon = new PassiveEventContainer(m_name == QStringLiteral("Markers")); // this is a speedup! if (m_eventListChanged) { m_eventListChanged = false; m_pevcon->setEventList(getEventGraphics()); } return m_pevcon; } const QString Item::path() const { if (m_parent == nullptr) return m_name; return QStringLiteral("%1/%2").arg(m_parent->path(), m_name); } const QString &Item::getName() const { return m_name; } Item *Item::getParent() const { return m_parent; } ItemList Item::getFullItemList(bool recursive) const { ItemList il; if (m_children == nullptr) return il; il += *m_children; // add current children list to list (dereference) if (!recursive) // if search is not recursive, done return il; auto end = m_children->constEnd(); for (auto i = m_children->constBegin(); i != end; ++i) // if recursive search is wanted => recursive getFullItemList calls to children and their children, a.s.o. { il += (*i)->getFullItemList(recursive); // probably yes.. ;) } return il; } ///* PRIVATE*/ const EventGraphicList Item::getEventGraphicsRecursive() const //{ // EventGraphicList egl; // // first modify the HIGHEST item, then the children items (or grouped events will be grouped again in higher items, which is not working) // //auto end = ; // for (auto i = m_children->constBegin(); i != m_children->constEnd(); ++i) // { // egl += (*i)->getEventGraphics(true); // true, as this method is for recursive search // } // return egl; //} const EventGraphicList Item::getCurrentEGLFromPevcon() const // .. without recreating one or refresh them { if (m_pevcon == nullptr) return EventGraphicList(); return m_pevcon->getEventGraphicList(); } const EventGraphicList Item::getEventGraphics() const // create active events and group events if they overlap in timeline view { EventGraphicList egl; EventList el = getFullEventList(true, false); // now get all events, but not hidden ones as we are going to draw all visible events only std::sort(el.begin(), el.end(), [](const Event *a, const Event *b) { return a->startTime() < b->startTime(); }); for (auto i1 = el.begin(); i1 != el.end(); ++i1) { Event *e1 = (*i1); if (e1->isDeleted()) continue; qint64 egStart = e1->startTime(); // list is sorted, so start time will never change in this process (so taking e1's start time is fine) qint64 egEnd = egStart + EventTools::getBlockSizeTime(e1->duration()); // don't use endTime directly as using the block size time on the duration is essential! qint64 blockSizeTime = EventTools::getBlockSizeTime(); bool egChanged = false; EventGraphic *eg = new EventGraphic(e1); egl += eg; auto i2 = i1 + 1; /* start at i (+1) as everything behind it is already done (list is sorted!) */ for (; i2 != el.end(); ++i2) { if (egChanged) { egEnd = egStart + eg->getDuration(); if (egEnd - egStart < blockSizeTime) // the collision check always refers to the complete event craphic - it must always be at least blockSizeTime wide egEnd = egStart + blockSizeTime; egChanged = false; } Event *e2 = (*i2); if (e2->isDeleted()) continue; qint64 start2 = e2->startTime(); if (start2 >= egStart && egEnd >= start2) // e2 is inside eg, put e2 into eg { eg->addEvent(e2); egChanged = true; } else // if this event is not inside the current eg, then the following events won't either - loop can be stopped here! { // [never is begin, as i2 was initialized with 'i1 + 1'] if (i2 != el.begin()) // the primary loop has to use this event as a new eg, but the iterator will be ++'ed and therefore skipped, so do a --, but -- on an interator seems ugly.. --i2; break; } } // i2 if (i2 == el.end()) // end if the secondary loop reached the end (must be done because the end of the loop would (probably) otherwise do a ++ on an invalid iterator) break; i1 = i2; // skip processed events (don't +1 here as the primary loop will do this anyways) } // i1 return egl; } EventList Item::getFullEventList(bool recursive, bool getHiddenEvents) const { EventList el; if (getHiddenEvents) { el = m_eventList; // add event list from this item first } else // only get visible events { el.reserve(m_eventList.count()); // TODO: spill some memory for a speedup ? //auto end = ; for (Event *e : m_eventList) { if (e == nullptr || !e->isVisible()) continue; el.append(e); } } if (!m_children || !recursive) // recursive search in EXISTING children list ? return el; for (const Item *item : qAsConst(*m_children)) el += item->getFullEventList(recursive, getHiddenEvents); return el; } void Item::setParent(Item *parent) { m_parent = parent; } void Item::addEvent(Event *event) // private! call TimelineModel::addEventToItem(...) to set possible latest event (by looking for the greates number in position) { m_eventList.append(event); setEventListChanged(); // say that the list has been changed and new event list has to be created when asked } void Item::setEventListChanged() { m_eventListChanged = true; if (m_parent != nullptr) { // tell the parent that his event list changed, too, because it has to show all its children's events, too m_parent->setEventListChanged(); } } bool Item::removeEvent(int eventId) { for (auto i = m_eventList.begin(); i != m_eventList.end(); ++i) { Event *event = *i; if (event->uniqueId() == eventId) { m_eventList.erase(i); setEventListChanged(); return true; } } return false; } void Item::addItem(Item *item) { if (!m_children) m_children = new ItemList(); item->setParent(this); // set given item's parent on this, because it was added to this item m_children->append(item); } bool Item::hasVisibleEvents() const { // check if this item itself has events .. if (!m_eventList.isEmpty() && std::find_if(m_eventList.constBegin(), m_eventList.constEnd(), [](Event *e) { return e->isVisible(); }) != m_eventList.constEnd()) return true; // .. no subitems, no chance to have events at this point if (m_children == nullptr) return false; // .. otherwise check if subitems have events, what makes this item have events, too, as the subitems' events are printed as a summary channel on this item for (auto i = m_children->constBegin(); i != m_children->constEnd(); ++i) { Item *item = *i; if (item->hasVisibleEvents()) return true; } // no subitem had events? then this item has definitely no events return false; } Item *Item::child(int row) const { if (m_children == nullptr) return nullptr; int idx = 0; // only show children that have events aka. are not invisible for (auto i = m_children->constBegin(); i != m_children->constEnd(); ++i) { Item *item = *i; if (item->hasVisibleEvents()) { if (row == idx) return item; else idx++; } } return nullptr; } int Item::childCount() const { if (m_children) return m_children->count(); return 0; } int Item::visibleChildCount() const { if (m_children == nullptr) return 0; int result = 0; for (auto i = m_children->constBegin(); i != m_children->constEnd(); ++i) { Item *item = *i; if (item->hasVisibleEvents()) result++; } return result; } ItemList *Item::getChildren() { return m_children; // returns 0 if no list exists } int Item::indexOfParent() const { if (m_parent == nullptr || m_parent->getChildren() == nullptr) return -1; int idx = 0; ItemList *parentItemList = m_parent->getChildren(); // only show children that have events aka. are not invisible for (auto i = parentItemList->constBegin(); i != parentItemList->constEnd(); ++i) { Item *item = *i; if (item == this) return idx; idx++; } Q_ASSERT(false); return -1; } } // namespace LtsTimelineClassic