560 lines
14 KiB
C++
Executable File
560 lines
14 KiB
C++
Executable File
#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 <QDebug>
|
|
#include <QJsonArray>
|
|
#include <QJsonObject>
|
|
#include <QJsonValue>
|
|
#include <QPainter>
|
|
#include <QBrush>
|
|
#include <QColor>
|
|
#include <QPen>
|
|
|
|
|
|
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<TrackEntry *> TrackWidget::getSelectedFilterEntries() const
|
|
{
|
|
QVector<TrackEntry *> result;
|
|
const QVector<FilterTrackWidget *> &ftws = filterLines();
|
|
|
|
for (const FilterTrackWidget *ftw : qAsConst(ftws))
|
|
{
|
|
const FilterBackgroundWidget *fbw = ftw->fbw();
|
|
const QMap<int /*id*/, TrackContentWidget *> &fcws = fbw->filterContentWidgets();
|
|
|
|
for (auto i = fcws.constBegin(); i != fcws.constEnd(); ++i)
|
|
{
|
|
TrackContentWidget *tcw = i.value();
|
|
result += tcw->getSelectedEntries();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
const QVector<TrackEntry *> &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<FilterTrackWidget *>(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<TrackEntry *> 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<TrackEntryType> &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<int /*id*/, TrackContentWidget *> &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<TrackEntry *> &filterEntries = datWidget->getTrackEntries();
|
|
|
|
for (const TrackEntry *filterTE : filterEntries)
|
|
{
|
|
if (filterTE == te)
|
|
return parentTopicId;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
QVector<TrackEntry *> TrackWidget::getTrackEntriesWithFilters(bool selectedEntriesOnly) const
|
|
{
|
|
QVector<TrackEntry *> result;
|
|
const QVector<TrackEntry *> 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<int /*id*/, TrackContentWidget *> &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<TrackEntry *> &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<TrackEntry *> TrackWidget::getTrackEntriesFiltersAsEntries(bool selectedEntriesOnly) const
|
|
{
|
|
QVector<TrackEntry *> result;
|
|
const QVector<TrackEntry *> 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<int /*id*/, TrackContentWidget *> &filterContentWidgets = ftw->fbw()->filterContentWidgets();
|
|
|
|
for (auto i1 = filterContentWidgets.constBegin(); i1 != filterContentWidgets.constEnd(); ++i1)
|
|
{
|
|
TrackContentWidget *tcw = i1.value();
|
|
result += tcw->getTrackEntries();
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
QVector<TrackEntry *> TrackWidget::getTrackEntries(bool selectedEntriesOnly) const
|
|
{
|
|
QVector<TrackEntry *> result;
|
|
const QVector<TrackEntry *> 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<TrackEntry *> &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
|