/** * @RDE_HEADER@ * widget in MVC pattern which manages the entire timeline to add on a layout or widget * $Id$ * $HeadURL$ * $Author$ * Created by Mike Wille, SLSE, 2014-10-01 */ #include "TimelineWidget.h" #include "EventDelegate.h" #include "ui_TimelineWidgetUi.h" #include "AbstractTimelineModel.h" #include "EventGraphic.h" #include #include #include #include #include #include namespace LtsTimelineClassic { TimelineWidget::TimelineWidget(QWidget *parent) // model cannot be set here, as qt has to create this Widget and does not know how to handle more parameters as the parent parameter : QWidget(parent) , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Timeline.Widget")) , m_timelineUi(new Ui::Timeline) , m_min(0) , m_max(1) , m_sbPos(0) , m_zoomFactor(1) // 100% , m_headerView(new TimelineHeaderView) , m_model(nullptr) { m_timelineUi->setupUi(this); m_timelineUi->view->setHeader(m_headerView); m_headerView->setMinimumSectionSize(150); m_timelineUi->scrollbar->setMouseTracking(true); connect(m_timelineUi->scrollbar, &QScrollBar::sliderReleased, this, &TimelineWidget::scrollBarFilter2); connect(m_timelineUi->scrollbar, &QScrollBar::actionTriggered, this, &TimelineWidget::scrollBarFilter1); connect(m_headerView, &QHeaderView::sectionResized, m_timelineUi->indicator, &TimelineIndicator::slotSectionResized); connect(m_headerView, &QHeaderView::sectionResized, this, &TimelineWidget::slotSectionResized); connect(m_timelineUi->view, &TimelineView::signalZoom, m_timelineUi->indicator, &TimelineIndicator::slotZoom); connect(m_timelineUi->view, &TimelineView::signalUnzoom, m_timelineUi->indicator, &TimelineIndicator::slotUnzoom); connect(m_timelineUi->view, &TimelineView::signalZoom, this, &TimelineWidget::signalZoomChanged); connect(m_timelineUi->view, &TimelineView::signalUnzoom, this, &TimelineWidget::signalZoomChanged); connect(m_timelineUi->indicator, &TimelineIndicator::signalZoomChanged, this, &TimelineWidget::signalZoomChanged); connect(m_timelineUi->indicator, &TimelineIndicator::signalSetZoomFactor, this, &TimelineWidget::slotSetZoomFactor); connect(m_timelineUi->view, &TimelineView::signalEventAction, this, &TimelineWidget::signalEventAction); // use old connect style because grrrr, EventGraphic ! connect(m_timelineUi->cbDrawCache, &QCheckBox::toggled, this, [this](bool toggled) { m_timelineUi->view->setDrawCache(toggled); update(); // schedule repaint }); connect(m_timelineUi->cbArm, &QCheckBox::toggled, this, &TimelineWidget::signalArm); connect(m_timelineUi->updateTimeline, &QPushButton::clicked, this, &TimelineWidget::slotUpdateTimeline); } TimelineWidget::~TimelineWidget() { delete m_timelineUi; } void TimelineWidget::setLiveTime(qint64 time) { m_headerView->setLiveTime(time); EventDelegate *ed = qobject_cast(m_timelineUi->view->itemDelegate()); if (ed == nullptr) { qWarning() << "TimelineWidget::setLiveTime: ed == nullptr"; return; } ed->setLiveTime(time); } void TimelineWidget::hideDebugWidget() { m_timelineUi->debugWidget->hide(); m_timelineUi->lbMinMax->hide(); } void TimelineWidget::knownRangesChanged(const QVector &newRanges) { EventDelegate *ed = qobject_cast(m_timelineUi->view->itemDelegate()); if (ed == nullptr) { WARNING_LOG(m_logger) << "ed == nullptr"; return; } m_timelineUi->view->setKnownRanges(newRanges); ed->setKnownRanges(newRanges); m_timelineUi->view->refreshData(); // calls update in both views (itself & header) } void TimelineWidget::setMaxPos() { m_timelineUi->scrollbar->setValue(m_timelineUi->scrollbar->maximum()); } int TimelineWidget::getMaxPos() const { return m_timelineUi->scrollbar->maximum(); } int TimelineWidget::getPos() const { return m_timelineUi->scrollbar->value(); } void TimelineWidget::setPos(qint64 pos) { m_timelineUi->scrollbar->setValue(pos / 1000); // in sec } void TimelineWidget::setPositionMarkerTime(const qint64 time) { m_timelineUi->view->slotSetPosMarkTime(time); } void TimelineWidget::slotUpdateTimeline() // called when either the scrollbar, zoom level or section is moved (refetch events!) { qint64 min = m_model->lowestPositionedItem(); qint64 max = m_model->highestPositionedItem(); if (m_timelineUi->cbMinMax->isChecked()) { QString minmax(QStringLiteral("%1 (%2) - %3 (%4)") .arg(QDateTime::fromMSecsSinceEpoch(min).toString()) .arg(min) .arg(QDateTime::fromMSecsSinceEpoch(max).toString()) .arg(max)); m_timelineUi->lbMinMax->setText(minmax); } setTimelineRange(min, max); emit signalRangeChanged(); } void TimelineWidget::setTimelineModel(AbstractTimelineModel *model) { m_model = model; connect(m_model, &AbstractTimelineModel::dataChanged, this, &TimelineWidget::slotRefreshData /* no need to recalc all values, just the data changed, just render them */); // redirect signalPlayback from model to widget (signal must now be connected to desired class from extern) m_timelineUi->view->setTimelineModel(m_model); // AddLine & ResetLineList connect(m_timelineUi->indicator, &TimelineIndicator::signalAddLine, m_timelineUi->view, &TimelineView::slotAddLine); connect(m_timelineUi->indicator, &TimelineIndicator::signalResetLineList, m_timelineUi->view, &TimelineView::slotResetLineList); EventDelegate *ed = qobject_cast(m_timelineUi->view->itemDelegate()); if (ed != nullptr) { connect(m_timelineUi->indicator, &TimelineIndicator::signalAddLine, ed, &EventDelegate::slotAddLine); connect(m_timelineUi->indicator, &TimelineIndicator::signalResetLineList, ed, &EventDelegate::slotResetLineList); } m_timelineUi->view->expandToDepth(1); m_headerView->resizeSection(0, m_headerView->minimumSectionSize()); // widget needs help to set minimum section size m_headerView->resizeSection(1, m_headerView->minimumSectionSize()); // s.a.a slotSetZoomFactor(m_timelineUi->indicator->zoomFactorPoints()); // initial z00m } qint64 TimelineWidget::getRangeMinimum() const { return EventTools::getAdjustedMin(); } qint64 TimelineWidget::getRangeMaximum() const { return EventTools::getAdjustedMax(); } void TimelineWidget::setTimelineRange(qint64 min, qint64 max) // also sets scrollbar range { m_min = min; m_max = max; if (m_min > 0 && m_max > 2) // only set scrollbar values if there are events { m_sbPos = EventTools::fromSecondsToMsecs(m_timelineUi->scrollbar->sliderPosition()); if (m_max <= m_min) { if (m_min > (10 * 60)) m_min -= (10 * 60); // be at least 10 minutes long (from the start until now) } else { qint64 diff = m_max - m_min; if (diff < (10 * 60)) if (m_min > (10 * 60)) m_min -= (10 * 60); // be at least 10 minutes long (from the start until now) if the difference between min & max is lower than 10 minutes } // sadly we cannot use getBlockSizeTime here, as setValues would invalidate everything calculated by getBlockSizeTime, because it uses values calculated by setValues, which gets called next.. // else // m_max = m_max - EventTools::s_diff + EventTools::getBlockSizeTime(0); // add one event width to the end, so that it is not cut off when live // NOTE: every calculation done before the call in the next line invalidates every value calculated by EventTools !! EventTools::setValues(m_min, m_max, m_sbPos, m_zoomFactor); //qWarning() << "set scrollbar range" << m_min << m_max; m_timelineUi->scrollbar->setRange(EventTools::fromMsecsToSeconds(EventTools::getTimelineMin()), EventTools::fromMsecsToSeconds(EventTools::getTimelineMax())); if (m_sbPos < m_min) // sbPos can still be smaller than min { m_sbPos = m_min; //qWarning() << "set slider pos" << m_sbPos; m_timelineUi->scrollbar->setSliderPosition(EventTools::fromMsecsToSeconds(m_sbPos)); } //qWarning() << "min" << EventTools::fromMsecsToSeconds(m_min) << "max" << EventTools::fromMsecsToSeconds(m_max) << "pagestep" << pagestep; int pagestep = EventTools::fromMsecsToSeconds(EventTools::s_diff); if (pagestep < 0) pagestep = 1; //qWarning() << "set scrollbar page step" << pagestep; m_timelineUi->scrollbar->setPageStep(pagestep); /* qWarning() << "scrollbar: min" << m_timelineUi->scrollbar->minimum() << "max" << m_timelineUi->scrollbar->maximum() << "pagestep" << m_timelineUi->scrollbar->pageStep() << "value" << m_timelineUi->scrollbar->value(); */ m_timelineUi->bsz->setText(QString::number(EventTools::getBlockSizeTime())); } refreshData(); } void TimelineWidget::slotRefreshData() { refreshData(); } void TimelineWidget::refreshData() { m_headerView->refreshData(); // calls update m_timelineUi->indicator->refreshData(); // ONLY calls update m_timelineUi->view->refreshData(); // calls update in both views (itself & header) } void TimelineWidget::slotSetZoomFactor(qint64 zoomFactor) { m_zoomFactor = zoomFactor; slotUpdateTimeline(); // reuse method to set new pagestep of scrollbar FIMXE: method "refreshScrollBar" to do this? } void TimelineWidget::scrollBarFilter1(int ) { scrollBarFilter2(); } void TimelineWidget::scrollBarFilter2() { slotUpdateTimeline(); // updates all (views) containers } void TimelineWidget::slotSectionResized(int logicalIndex, int oldSize, int newSize) { (void)oldSize; if (logicalIndex == 1) // event section [should always be called, even if col0 was resized, as the last column is always strechted, there would be no space for col0 to take that was not occupied by col1] { int viewScrollbarWidth = m_timelineUi->view->verticalScrollBar()->width(); m_timelineUi->scrollbar->setMinimumHeight(viewScrollbarWidth); // set scrollbar to same size as scrollbar in view m_timelineUi->scrollbar->setMaximumHeight(viewScrollbarWidth); // set scrollbar to same size as scrollbar in view m_timelineUi->scrollbar->setMaximumWidth(newSize); m_timelineUi->scrollbar->setMinimumWidth(newSize); EventTools::setSection(m_headerView->sectionPosition(1), newSize /* as it was not yet set in m_headerView->sectionSize(1) ! */); } slotUpdateTimeline(); } } // namespace LtsTimelineClassic