/** * @RDE_HEADER@ * MVC view (QTreeView) - shows items (rows), events, date-time lines and more * $Id$ * $HeadURL$ * $Author$ * Created by Mike Wille, SLSE, 2014-10-01 */ #include "TimelineView.h" #include "TimelineHeaderView.h" #include "TimelineWidget.h" #include "EventDelegate.h" #include "EventGraphic.h" #include #include #include #include #include #include #include #include #include namespace LtsTimelineClassic { //TimelineView::TimelineView(TimelineHeaderView* headerView, QWidget *parent) : TimelineView::TimelineView(QWidget *parent) : QTreeView(parent) //, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.View")) , m_model(nullptr) , m_drawCache(false) { m_posMarkTime = 0; // invalid - do not draw //setMouseTracking(true); setAlternatingRowColors(true); // so we can differ between specific rows setEditTriggers(QAbstractItemView::NoEditTriggers); setSelectionBehavior(QAbstractItemView::SelectItems); setSelectionMode(QAbstractItemView::NoSelection); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // no horizontal scroll bar, because the TimelineWidget manages this and events are only virtually drawn on the view setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } void TimelineView::setHeader(QHeaderView *header) { if (header) { QTreeView::setHeader(header); // cannot use a member attribute, because setHeader takes ownership of given header, very cautious! please use qobject_cast when TimelineHeaderView needed header->setMinimumSectionSize(150); // minimum size for visualisation issues } else { //qDebug() << "ERROR: invalid header!"; } } AbstractTimelineModel *TimelineView::timelineModel() { return m_model; } void TimelineView::setTimelineModel(AbstractTimelineModel *model) { m_model = model; setMouseTracking(true); // if it is not enabled by default setModel(m_model); EventDelegate *ed = new EventDelegate(this); EventTools::setSectionHeight(ed->sizeHint().height()); setItemDelegate(ed); // a delegate, only for column 1 (events-column) } EventGraphic *TimelineView::getEventOnPosition(QPoint position) { position = viewport()->mapFromGlobal(position); QModelIndex index = indexAt(position); if (index.isValid() && position.x() > header()->sectionSize(0)) // do not allow clicking on section 0 { Item *item = static_cast(index.internalPointer()); if (!item) return nullptr; qint64 curTime = EventTools::getEventTime(position.x()); EventGraphicList egl = item->getCurrentEGLFromPevcon(); // must be the current egl from pevcon (not eventlist), as events can be grouped (stacked events) qint64 blockSizeTime = EventTools::getBlockSizeTime(); for (int i = 0; i < egl.count(); i++) { EventGraphic *eg = egl.at(i); if (!eg) continue; qint64 eventStartTime = eg->lowestTime(); if (eventStartTime == 0) continue; qint64 eventEndTime = eventStartTime; qint64 eglDuration = eg->getDuration(); if (eglDuration > blockSizeTime) eventEndTime += eglDuration; else eventEndTime += blockSizeTime; if (curTime >= eventStartTime && curTime <= eventEndTime) return eg; } } return nullptr; } bool TimelineView::event(QEvent *event) { switch (event->type()) { case QEvent::ToolTip: { //emit signalEventAction(getEventOnPosition(((QHelpEvent*)static_cast(event))->globalPos()), EventGraphic::ACTION_TOOLTIP); QHelpEvent *helpEvent = static_cast(event); if (!helpEvent) return true; EventGraphic *eg = getEventOnPosition(helpEvent->globalPos()); if (!eg) return true; emit signalEventAction(eg, EventGraphic::ACTION_TOOLTIP); return true; } default: { break; } } return QTreeView::event(event); // otherwise... } void TimelineView::wheelEvent(QWheelEvent *event) { QTreeView::wheelEvent(event); if (QGuiApplication::queryKeyboardModifiers() != Qt::ControlModifier) // only scroll when CTRL is pressed return; int scrolls = event->delta() / 120; if (scrolls > 0) { for (int i = 0; i < scrolls; i++) emit signalZoom(); } if (scrolls < 0) { for (int i = 0; i > scrolls; i--) emit signalUnzoom(); } } void TimelineView::mouseReleaseEvent(QMouseEvent *event) { QTreeView::mouseReleaseEvent(event); QPoint position = event->globalPos(); EventGraphic *eg = getEventOnPosition(position); if (!eg) return; switch (event->button()) { case Qt::LeftButton: { emit signalEventAction(eg, EventGraphic::ACTION_LEFT_CLICKED); break; } case Qt::RightButton: { emit signalEventAction(eg, EventGraphic::ACTION_RIGHT_CLICKED); break; } default: { break; } } } void TimelineView::slotAddLine(int posX, qint64 time) // add posX to a list, so that the view can draw them on itself { //m_lineList.append(QPair(posX, time)); m_lineMap.insert(posX, time); // HACK! position as key, so only one line per position (which is perfectly okay and speedy) } void TimelineView::slotResetLineList() { m_lineMap.clear(); } /*void TimelineView::slotSetPosMarkPos(int posMarkPos) // hint: must be INT, because it's the position as PIXELS (not time as qint64) { //int sectionSize = this->header()->sectionSize(1); //QRect sectionRect(0, 0, sectionSize, 0); // only width matters for calculating a pixel position dependent on a fixed position qint64 posMarkTime = EventTools::getEventTime(posMarkPos); m_posMarkTime = posMarkTime; TimelineHeaderView* headerView = qobject_cast(header()); if (headerView) { headerView->setPosMarkTime(posMarkTime); } refreshData(); }*/ void TimelineView::slotSetPosMarkTime(qint64 posMarkTime) // does not have to be a slot anymore !! { m_posMarkTime = posMarkTime; TimelineHeaderView *headerView = qobject_cast(header()); if (headerView) headerView->setPosMarkTime(posMarkTime); refreshData(); } void TimelineView::refreshData() { update(); viewport()->update(); } void TimelineView::paintEvent(QPaintEvent *event) // draw lines to show where next texts are { if (!m_model) // cannot draw without a model return; QWidget *viewp0rt = viewport(); //qWarning() << "viewp0rt ptr" << (unsigned long long)viewp0rt; QPainter painter(viewp0rt); if (!painter.isActive()) { //qWarning() << "painter not active"; } painter.save(); int viewPortHeight = viewp0rt->height(); QHeaderView *head0r = header(); //qWarning() << "head0r ptr" << (unsigned long long)head0r; int sectionPos = head0r->sectionPosition(1); int sectionSize = head0r->sectionSize(1); int minSection = sectionPos; // minimum x position int maxSection = minSection + sectionSize; // maximum x position //QRect paintRect(head0r->sectionPosition(1), 0, // head0r->sectionSize(1), height()); QRect paintRect(minSection, 0, maxSection - minSection, height()); painter.setClipping(true); painter.setClipRect(paintRect); // draw date text lines first, can be overdrawn //for (int i = 0; i < m_lineList.count(); i++) for (auto i = m_lineMap.constBegin(); i != m_lineMap.constEnd(); ++i) { int pos = i.key(); //m_lineList.at(i).first; painter.setPen(QPen(QBrush(QGuiApplication::palette().shadow()), 1.0)); if (pos > minSection && pos < maxSection) // do not draw if out of view painter.drawLine(pos, 0, pos, viewPortHeight); else { #ifdef VERBOSE qCritical() << "out of view => pos" << pos << "min" << minSection << "max" << maxSection; #endif } } // workaround for date text lines: first draw lines, then draw containers (which are transparent) painter.restore(); QTreeView::paintEvent(event); painter.save(); if (m_drawCache) { // draw gray bg (indicate uncached ranges) painter.setPen(QPen(QBrush("gray"), 1)); painter.setBrush(QBrush("gray")); painter.drawRect(minSection, 0, maxSection, height()); // draw cached ranges painter.setPen(QPen(QBrush("white"), 1)); painter.setBrush(QBrush("white")); int h = height(); for (const auto &range : qAsConst(m_knownRanges)) { int start = EventTools::getEventPosition(range.start); if (start < minSection) start = minSection; int end = EventTools::getEventPosition(range.end); if (end > maxSection) end = maxSection; painter.drawRect(start, 0, end - start, h); } } // draw marker lines // QRect areaRect(sectionPos, viewp0rt->x(), // sectionSize, height()); //EventTools::setValues(m_min, m_max, m_sbPos, m_zoomFactor); // DO NOT SET VALUES HERE!!! Because old values are needed, plus this saves CPU & RAM ItemList il = m_model->getRootItem()->getFullItemList(true); for (int i = 0; i < il.count(); i++) { if (il.at(i)->getName() == tr("Markers")) EventTools::drawMarkLines(&painter, il.at(i)->getFullEventList(true), height());//, areaRect); } //QRect sectionRect(0, 0, header()->sectionSize(1), 0); // draw pos mark line - is more important than a marker line if (m_posMarkTime > 0) // != 0 { int posMarkPosition = EventTools::getEventPosition(m_posMarkTime); // pretend that the posMark is an event, does not matter if (posMarkPosition > sectionPos && posMarkPosition < sectionSize + sectionPos) { painter.setPen(QPen(QGuiApplication::palette().highlight(), 1.0)); int xpos = posMarkPosition;// + sectionPos; painter.drawLine(xpos, 0, xpos, height()); } } painter.restore(); //event->ignore(); } }