#include "track_widget.h" #include "ui_track_widget.h" #include "track_entry.h" #include "filter_track_widget.h" #include "filter_background_widget.h" #include "track_content_widget.h" #include "tools.h" #include #include #include #include #include #include #include #include namespace LtsScriptTimeline { TrackWidget::TrackWidget(int baseWidth, bool readonly, Tools *tools, bool filtersAllowed, QWidget *parent) : QWidget(parent) , ui(new Ui::TrackWidget) , m_baseWidth(baseWidth) , m_readonly(readonly) , m_tools(tools) { Q_ASSERT(m_tools != nullptr); ui->setupUi(this); ui->mainTrack->setTrackWidget(this); ui->mainTrack->setTools(tools); ui->chkSelect->hide(); ui->filtersWidget->hide(); ui->leName->hide(); ui->pbApply->hide(); ui->pbCancel->hide(); ui->pbEditName->hide(); // not needed anymore if (!filtersAllowed) ui->pbExpand->hide(); ui->pbAddFilter->hide(); setBaseWidth(baseWidth); setReadonly(readonly); connect(ui->mainTrack, &TrackContentWidget::signalUnselectAll, this, &TrackWidget::signalUnselectAll); connect(ui->mainTrack, &TrackContentWidget::signalTrackEntrySelectionChanged, this, &TrackWidget::signalTrackEntrySelectionChanged); connect(ui->mainTrack, &TrackContentWidget::signalChangesMade, this, &TrackWidget::signalChangesMade); connect(ui->mainTrack, &TrackContentWidget::signalChangesMade, this, &TrackWidget::slotMainChangesMade); connect(ui->mainTrack, &TrackContentWidget::signalTrackEntryAdded, this, &TrackWidget::slotTrackEntryAdded); connect(ui->mainTrack, &TrackContentWidget::signalTrackEntryDeleted, this, &TrackWidget::slotTrackEntryDeleted); connect(ui->mainTrack, &TrackContentWidget::signalEditEntry, this, &TrackWidget::signalEditEntry); connect(ui->chkSelect, &QCheckBox::stateChanged, this, &TrackWidget::signalSelectTrack); connect(ui->pbExpand, &QPushButton::clicked, this, &TrackWidget::slotExpand); connect(ui->pbAddFilter, &QPushButton::clicked, this, &TrackWidget::slotAddFilter); } TrackWidget::~TrackWidget() { m_filterLines.clear(); delete ui; } void TrackWidget::setReadonly(bool readonly) { m_readonly = readonly; ui->mainTrack->setReadonly(readonly); if (readonly) ui->pbAddFilter->hide(); // hide if readonly else if (ui->filtersWidget->isVisible()) // only show if expanded ui->pbAddFilter->show(); for (auto i3 = m_filterLines.begin(); i3 != m_filterLines.end(); ++i3) { FilterTrackWidget *ftw = *i3; ftw->setReadonly(readonly); } } void TrackWidget::setScriptTimeline(ScriptTimeline *scriptTimeline) { m_scriptTimeline = scriptTimeline; ui->mainTrack->setScriptTimeline(m_scriptTimeline); } void TrackWidget::keyPressEvent(QKeyEvent *event) { getTrackContentWidget()->keyPressEvent(event); } QVector TrackWidget::getSelectedFilterEntries() const { QVector result; const QVector &ftws = filterLines(); for (const FilterTrackWidget *ftw : qAsConst(ftws)) { const FilterBackgroundWidget *fbw = ftw->fbw(); const QMap &fcws = fbw->filterContentWidgets(); for (auto i = fcws.constBegin(); i != fcws.constEnd(); ++i) { TrackContentWidget *tcw = i.value(); result += tcw->getSelectedEntries(); } } return result; } const QVector &TrackWidget::getSelectedEntries() const { return ui->mainTrack->getSelectedEntries(); // ignores filters } void TrackWidget::slotAddFilter() { FilterTrackWidget *ftw = new FilterTrackWidget(m_baseWidth, m_readonly); ftw->setTrackWidget(this); ftw->setTools(m_tools); ftw->setScriptTimeline(m_scriptTimeline); ui->filtersWidget->layout()->addWidget(ftw); m_filterLines.append(ftw); connect(ftw, &FilterTrackWidget::signalChangesMade, this, &TrackWidget::signalChangesMade); connect(ftw, &FilterTrackWidget::signalUnselectAll, this, &TrackWidget::signalUnselectAll); connect(ftw, &FilterTrackWidget::signalTrackEntrySelectionChanged, this, &TrackWidget::signalTrackEntrySelectionChanged); connect(ftw, &FilterTrackWidget::signalEditEntry, this, &TrackWidget::signalEditEntry); connect(ftw, &FilterTrackWidget::signalTrackEntryDeleted, this, &TrackWidget::signalTrackEntryDeleted); connect(ftw, &FilterTrackWidget::signalDeleteMe, this, [this]() { m_filterLines.removeAll(qobject_cast(sender())); // no qobject_cast for destroyed objects sender()->deleteLater(); emit signalChangesMade(); }); emit signalChangesMade(); slotMainChangesMade(); // so that the new FBW also contains all TCW } void TrackWidget::slotMainChangesMade() { const QVector trackEntries = ui->mainTrack->getTrackEntries(); for (auto i1 = trackEntries.constBegin(); i1 != trackEntries.constEnd(); ++i1) { const TrackEntry *te = *i1; for (auto i3 = m_filterLines.begin(); i3 != m_filterLines.end(); ++i3) { FilterTrackWidget *ftw = *i3; ftw->fbw()->adjustFilterContentWidgets(te); // adjusts if needed } } // TODO: check for dead FTWs } void TrackWidget::slotTrackEntryAdded(TrackEntry *te) // create a FilterTrack for exactly that TE { //qWarning() << "slotTrackEntryAdded"; for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) ftw->fbw()->trackEntryAdded(te); emit signalTrackEntryAdded(te); } void TrackWidget::slotTrackEntryDeleted(const int topicId) { //qWarning() << "slotTrackEntryDeleted"; for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) ftw->fbw()->trackEntryDeleted(topicId); emit signalTrackEntryDeleted(topicId); } //void TrackWidget::setAllowedTypes(const QVector &allowedTypes) //{ // ui->mainTrack->setAllowedTypes(allowedTypes); //} void TrackWidget::slotExpand() { if (ui->filtersWidget->isVisible()) { ui->pbExpand->setText(tr("More")); // FIXME: retranslateable on demand ui->pbAddFilter->hide(); ui->filtersWidget->hide(); } else { ui->pbExpand->setText(tr("Less")); // FIXME: retranslateable on demand if (!m_readonly) // filters not addable if readonly ui->pbAddFilter->show(); ui->filtersWidget->show(); slotMainChangesMade(); } } void TrackWidget::selectEntry(int topicId) { ui->mainTrack->selectEntry(topicId); } int TrackWidget::getParentTopicIdOfFilter(const TrackEntry *te) const { // get all filters and put them into that json format for (const FilterTrackWidget *ftw : m_filterLines) { const FilterBackgroundWidget *fbw = ftw->fbw(); const QMap &filterContentWidgets = fbw->filterContentWidgets(); // get the TCW for exactly this topic for (auto i = filterContentWidgets.constBegin(); i != filterContentWidgets.constEnd(); ++i) { int parentTopicId = i.key(); const TrackContentWidget *datWidget = i.value(); const QVector &filterEntries = datWidget->getTrackEntries(); for (const TrackEntry *filterTE : filterEntries) { if (filterTE == te) return parentTopicId; } } } return -1; } QVector TrackWidget::getTrackEntriesWithFilters(bool selectedEntriesOnly) const { QVector result; const QVector trackEntryList = ui->mainTrack->getTrackEntries(); for (TrackEntry *te : trackEntryList) { if (selectedEntriesOnly && !ui->mainTrack->getSelectedEntries().contains(te)) continue; result += te; // get filters of this te as this te's detail data QJsonArray jsonFilterLines; // get all filters and put them into json format for (const FilterTrackWidget *ftw : qAsConst(m_filterLines)) { const FilterBackgroundWidget *fbw = ftw->fbw(); const QMap &filterContentWidgets = fbw->filterContentWidgets(); // get the TCW for exactly this topic const TrackContentWidget *datWidget = filterContentWidgets.value(te->m_topicId); if (datWidget == nullptr) // no filters available continue; const QVector &filterEntries = datWidget->getTrackEntries(); if (filterEntries.isEmpty()) continue; QJsonArray jsonFilterLine; // save to jsonFilterLines !! for (const TrackEntry *fte : filterEntries) jsonFilterLine.append(fte->toJson()); jsonFilterLines.append(jsonFilterLine); } te->m_detailData[QStringLiteral("Filters")] = jsonFilterLines; } return result; } QVector TrackWidget::getTrackEntriesFiltersAsEntries(bool selectedEntriesOnly) const { QVector result; const QVector trackEntryList = ui->mainTrack->getTrackEntries(); for (TrackEntry *te : trackEntryList) { if (selectedEntriesOnly && !ui->mainTrack->getSelectedEntries().contains(te)) continue; result += te; // get filters of this te as discrete tes for (FilterTrackWidget *ftw : m_filterLines) { const QMap &filterContentWidgets = ftw->fbw()->filterContentWidgets(); for (auto i1 = filterContentWidgets.constBegin(); i1 != filterContentWidgets.constEnd(); ++i1) { TrackContentWidget *tcw = i1.value(); result += tcw->getTrackEntries(); } } } return result; } QVector TrackWidget::getTrackEntries(bool selectedEntriesOnly) const { QVector result; const QVector trackEntryList = ui->mainTrack->getTrackEntries(); if (selectedEntriesOnly) { for (TrackEntry *te : trackEntryList) { if (!ui->mainTrack->getSelectedEntries().contains(te)) continue; result += te; } } else { return trackEntryList; } return result; } void TrackWidget::setBaseWidth(int width) { m_baseWidth = width; ui->mainLeft->setMinimumWidth(width); ui->mainLeft->setMaximumWidth(width); // TODO: better solution ? for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) ftw->setBaseWidth(m_baseWidth); } void TrackWidget::addCorrespondingTrackWidget(TrackContentWidget *pCorrespondingTrackWidget) { ui->mainTrack->addCorrespondingTrackWidget(pCorrespondingTrackWidget); } TrackContentWidget *TrackWidget::getTrackContentWidget() const { return ui->mainTrack; } void TrackWidget::setInternalName(const QString &name) { m_internalName = name; ui->mainTrack->setTrackName(name); } void TrackWidget::setTrackName(const QString &name) { ui->lblTrackName->setText(name); } bool TrackWidget::addTrackEntry(TrackEntry *te, bool selectTrackEntry) { if (!ui->mainTrack->addTrackEntry(te, selectTrackEntry)) // pass-through return false; const QJsonObject detailData = te->m_detailData; // detail data gets reset in the following method - I have no time looking for where this happens, this is a cheaper solution // at this point it's all about the filters of this topic: ////// CODE DUPLICATE // take filters from TEs detail data and put these filters on the filter lines (after creating them) // check if there is a json if (detailData.isEmpty()) { //qWarning() << "TrackWidget::addTrackEntry: topic has no detail data"; return true; } // check if there is a *valid* json // look for filters const QJsonArray filterLines = detailData[QStringLiteral("Filters")].toArray(); //qWarning() << "filterLines count" << filterLines.count(); if (filterLines.isEmpty()) return true; // no filters, nothing to do //qWarning() << "topic has filter line(s)!"; // create x filter lines int missingFilterLines = filterLines.count() - m_filterLines.count(); //qWarning() << "missingFilterLines" << missingFilterLines; while (missingFilterLines > 0) { slotAddFilter(); missingFilterLines--; } // create filter-TEs from TE for (int i1 = 0; i1 != filterLines.count(); i1++) { const QJsonArray filterLine = filterLines.at(i1).toArray(); for (const QJsonValue &filter : filterLine) { TrackEntry *filterTe = new TrackEntry; filterTe->fromJson(filter.toObject()); // add filter-TEs to coresponding filter line m_filterLines.at(i1)->addFilterEntry(te /* main topic */, filterTe/* filter topic */); //qWarning() << "added filter te" << filterTe->m_topicId; //qWarning() << "added a filter entry"; } } emit signalChangesMade(); return true; } QString TrackWidget::trackName() const { return m_internalName; //return ui->lblTrackName->text(); } bool TrackWidget::isSelected() const { return (ui->chkSelect->checkState() == Qt::CheckState::Checked); } void TrackWidget::unselectAll() { ui->mainTrack->unselectAll(); for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) { const auto fcw = ftw->fbw()->filterContentWidgets(); for (TrackContentWidget *tcw : fcw) tcw->unselectAll(); } } void TrackWidget::updateMe() { update(); ui->mainTrack->update(); slotMainChangesMade(); } void TrackWidget::setIndicatorTime(int time) { ui->mainTrack->setIndicatorTime(time); for (FilterTrackWidget *ftw : qAsConst(m_filterLines)) { const auto filterContentWidgets = ftw->fbw()->filterContentWidgets(); // indicator time for filter tcws must be extracted by their start time (their main te's start time) for (auto i1 = filterContentWidgets.begin(); i1 != filterContentWidgets.end(); ++i1) { int mainTeId = i1.key(); int mainTeStart = 0; const QVector &tes = ui->mainTrack->getTrackEntries(); for (const TrackEntry *te : tes) { if (te->m_topicId == mainTeId) { mainTeStart = te->m_startTime; break; } } TrackContentWidget *filterTcw = i1.value(); filterTcw->setIndicatorTime(time - mainTeStart); } } } void TrackWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.save(); painter.setPen(QGuiApplication::palette().windowText().color()); painter.setBrush(QGuiApplication::palette().window().color()); painter.drawRect(rect());//.adjusted(0, 0, -1, 0)); // don't adjust y, do not draw bottom line, looks better and is easier to draw :).adjusted(0, 0, -1, -1)); painter.restore(); } } // namespace LtsScriptTimeline