- Formatted all CodeGen module files (9 files) - Formatted LtsAppDescriptor module files - Formatted LtsAppManager/AppChooser files - Applied clang-format to all DTC and enum headers across modules - Removed coordTransTests from CommonUnitTestsPlaceholder (moved to tests/) Cosmetic changes only, no functional modifications. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1051 lines
29 KiB
C++
1051 lines
29 KiB
C++
#include "script_timeline.h"
|
|
|
|
#include "filter_background_widget.h"
|
|
#include "filter_track_widget.h"
|
|
#include "temporary_pointer_vector.h"
|
|
#include "tools.h"
|
|
#include "track_content_widget.h"
|
|
#include "track_entry.h"
|
|
#include "track_widget.h"
|
|
#include "ui_script_timeline.h"
|
|
|
|
#include <LtsWidgets/lts_message_dialog.h>
|
|
#include <QCursor>
|
|
#include <QDebug>
|
|
#include <QMessageBox>
|
|
#include <QPixmap>
|
|
#include <QRubberBand>
|
|
#include <QTime>
|
|
#include <QTimer>
|
|
#include <utility>
|
|
|
|
namespace LtsScriptTimeline
|
|
{
|
|
|
|
ScriptTimeline::ScriptTimeline(QWidget* parent)
|
|
: QWidget(parent), ui(new Ui::ScriptTimeline), m_uniqueId(0), m_scriptId(-1), m_baseTrackWidgetWidth(150),
|
|
m_readonly(false), m_time(0), m_tools(new Tools(this)), m_mlmsMaster(nullptr), m_ignoreChanges(false)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
this->setFocusPolicy(Qt::StrongFocus);
|
|
|
|
ui->indicator->setTools(m_tools);
|
|
|
|
ui->trackScrollBar->setMinimum(0);
|
|
ui->trackScrollBar->setMaximum(5 * 60);
|
|
|
|
connect(ui->zoomSlider, &QSlider::valueChanged, this, &ScriptTimeline::slotZoomValueChanged);
|
|
|
|
connect(ui->trackScrollBar, &QScrollBar::sliderReleased, this, &ScriptTimeline::slotScrollbarValueChanged);
|
|
connect(ui->trackScrollBar, &QScrollBar::actionTriggered, this, &ScriptTimeline::slotScrollbarValueChanged);
|
|
|
|
connect(ui->indicator, &Indicator::signalHandleResized, this, &ScriptTimeline::slotHeaderHandleResized);
|
|
connect(ui->indicator, &Indicator::signalHeaderIndicatorTimeChanged, this,
|
|
&ScriptTimeline::slotHeaderIndicatorTimeChanged);
|
|
connect(ui->indicator, &Indicator::signalHeaderIndicatorTimeUserChanged, this,
|
|
&ScriptTimeline::signalHeaderIndicatorTimeUserChanged);
|
|
|
|
QTimer* t = new QTimer(this);
|
|
t->setInterval(1500);
|
|
connect(t, &QTimer::timeout, this, &ScriptTimeline::slotSetNewScrollBarMaximum);
|
|
t->start();
|
|
|
|
slotZoomValueChanged(ui->zoomSlider->value());
|
|
}
|
|
|
|
ScriptTimeline::~ScriptTimeline()
|
|
{
|
|
m_copyTopics.clear();
|
|
m_ignoreChanges = true;
|
|
qDeleteAll(m_clipboard);
|
|
delete ui;
|
|
}
|
|
|
|
void ScriptTimeline::selectAll()
|
|
{
|
|
const QList<TrackWidget*> vecTrackWidgets = getTrackWidgets();
|
|
for (TrackWidget* tw : vecTrackWidgets)
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
tcw->selectAll();
|
|
}
|
|
}
|
|
|
|
bool ScriptTimeline::checkNewLength(TrackEntry* te, int newLength)
|
|
{
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
TrackContentWidget* tesTcw = nullptr;
|
|
|
|
for (TrackWidget* tw : tws)
|
|
{
|
|
const QVector<TrackEntry*>& tes = tw->getTrackContentWidget()->getTrackEntries();
|
|
|
|
auto it =
|
|
std::find_if(tes.begin(), tes.end(), [te](const TrackEntry* a) { return (a->m_topicId == te->m_topicId); });
|
|
|
|
if (it == tes.end())
|
|
continue;
|
|
|
|
tesTcw = tw->getTrackContentWidget();
|
|
break;
|
|
}
|
|
|
|
if (tesTcw == nullptr)
|
|
return false;
|
|
|
|
TrackEntry newTe(te);
|
|
newTe.m_length = newLength;
|
|
return tesTcw->checkForCollisionsSimple(newTe);
|
|
}
|
|
|
|
void ScriptTimeline::lockIndicator(bool lock)
|
|
{
|
|
ui->indicator->lockIndicator(lock);
|
|
}
|
|
|
|
void ScriptTimeline::keyPressEvent(QKeyEvent* event)
|
|
{
|
|
if (event->key() == Qt::Key_Delete)
|
|
{
|
|
slotDeleteSelectedTopics();
|
|
}
|
|
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_C)
|
|
{
|
|
slotCopyTopics();
|
|
}
|
|
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_V)
|
|
{
|
|
slotPasteTopics();
|
|
}
|
|
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_X)
|
|
{
|
|
slotCutTopics();
|
|
}
|
|
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_A)
|
|
{
|
|
selectAll();
|
|
}
|
|
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_S)
|
|
{
|
|
emit signalSave();
|
|
}
|
|
else
|
|
{
|
|
const QList<TrackWidget*> vecTrackWidgets = getTrackWidgets();
|
|
for (auto w : vecTrackWidgets)
|
|
w->keyPressEvent(event);
|
|
}
|
|
}
|
|
|
|
void ScriptTimeline::mousePressEvent(QMouseEvent* event)
|
|
{
|
|
if (event->buttons() != Qt::LeftButton)
|
|
return;
|
|
slotStartRubberband(event->pos());
|
|
}
|
|
|
|
void ScriptTimeline::mouseMoveEvent(QMouseEvent*)
|
|
{
|
|
slotModifyRubberband();
|
|
}
|
|
|
|
void ScriptTimeline::mouseReleaseEvent(QMouseEvent*)
|
|
{
|
|
const QList<TrackWidget*> vecTrackWidgets = getTrackWidgets();
|
|
for (auto w : vecTrackWidgets)
|
|
w->unselectAll();
|
|
|
|
slotStopRubberband();
|
|
}
|
|
|
|
void ScriptTimeline::slotScroll(int incr)
|
|
{
|
|
int oldValue = ui->trackScrollBar->value();
|
|
ui->trackScrollBar->setValue(oldValue + incr);
|
|
slotScrollbarValueChanged();
|
|
}
|
|
|
|
void ScriptTimeline::setReadonly(bool readonly)
|
|
{
|
|
m_readonly = readonly;
|
|
|
|
const QList<TrackWidget*> vecTrackWidgets = getTrackWidgets();
|
|
for (TrackWidget* tw : vecTrackWidgets)
|
|
tw->setReadonly(readonly);
|
|
}
|
|
|
|
void ScriptTimeline::clear()
|
|
{
|
|
m_copyTopics.clear();
|
|
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
|
|
for (TrackWidget* tw : trackWidgetList)
|
|
delete tw;
|
|
|
|
qDeleteAll(m_clipboard);
|
|
m_clipboard.clear();
|
|
|
|
m_uniqueId = 0;
|
|
|
|
updateEverything();
|
|
}
|
|
|
|
void ScriptTimeline::slotSetNewScrollBarMaximum()
|
|
{
|
|
int max = 5 * 60;
|
|
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (const TrackWidget* tw : trackWidgetList)
|
|
{
|
|
const QVector<TrackEntry*>& trackEntries = tw->getTrackContentWidget()->getTrackEntries();
|
|
|
|
for (const TrackEntry* te : trackEntries)
|
|
{
|
|
int maxTeTime = te->m_startTime + te->m_length;
|
|
|
|
if (maxTeTime > max)
|
|
{
|
|
max = maxTeTime + 5 * 60;
|
|
}
|
|
}
|
|
}
|
|
|
|
ui->trackScrollBar->setMaximum(max);
|
|
ui->trackScrollBar->setPageStep(60);
|
|
m_tools->m_timelineLength = max;
|
|
|
|
updateEverything();
|
|
}
|
|
|
|
void ScriptTimeline::setIndicatorTime(int time)
|
|
{
|
|
ui->indicator->setIndicatorTime(time);
|
|
}
|
|
|
|
int ScriptTimeline::getCurrentScriptEndTime()
|
|
{
|
|
int currEndTime = 0;
|
|
const QList<TrackWidget*> listTrackWidgets = getTrackWidgets();
|
|
for (const auto ltw : listTrackWidgets)
|
|
{
|
|
const auto cont = ltw->getTrackEntries();
|
|
for (const auto pTE : cont)
|
|
{
|
|
int endTime = pTE->m_startTime + pTE->m_length;
|
|
currEndTime = qMax(currEndTime, endTime);
|
|
}
|
|
}
|
|
|
|
return currEndTime;
|
|
}
|
|
|
|
void ScriptTimeline::slotHeaderIndicatorTimeChanged(int time)
|
|
{
|
|
m_time = time;
|
|
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (TrackWidget* tw : trackWidgetList)
|
|
tw->setIndicatorTime(time);
|
|
|
|
emit signalHeaderIndicatorTimeChanged(time);
|
|
}
|
|
|
|
void ScriptTimeline::slotHeaderHandleResized(int newSize)
|
|
{
|
|
m_baseTrackWidgetWidth = newSize;
|
|
|
|
updateEverything();
|
|
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (TrackWidget* tw : trackWidgetList)
|
|
tw->setBaseWidth(newSize);
|
|
}
|
|
|
|
void ScriptTimeline::slotDeleteTracks()
|
|
{
|
|
int count = 0;
|
|
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (const TrackWidget* tw : trackWidgetList)
|
|
{
|
|
if (tw->isSelected())
|
|
count++;
|
|
}
|
|
|
|
if (count < 1)
|
|
{
|
|
QApplication::beep();
|
|
return;
|
|
}
|
|
|
|
if (QMessageBox::question(this, tr("Delete selected tracks"),
|
|
tr("Are you sure you want to delete all %1 tracks?").arg(count),
|
|
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
|
|
return;
|
|
|
|
for (TrackWidget* tw : trackWidgetList)
|
|
{
|
|
if (tw->isSelected())
|
|
tw->deleteLater();
|
|
}
|
|
}
|
|
|
|
void ScriptTimeline::slotZoomValueChanged(int value)
|
|
{
|
|
if (value < 1)
|
|
value = 1;
|
|
|
|
m_tools->m_zoomFactor = value;
|
|
|
|
updateEverything();
|
|
}
|
|
|
|
void ScriptTimeline::resizeEvent(QResizeEvent* event)
|
|
{
|
|
QWidget::resizeEvent(event);
|
|
|
|
{
|
|
QPixmap p(QStringLiteral(":/style/portrait-mode.svg"));
|
|
int h = ui->trackScrollBar->height();
|
|
QSize s(h, h);
|
|
ui->lbPortrait->setPixmap(p.scaled(s, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
|
ui->lbPortrait->setFixedSize(s);
|
|
}
|
|
{
|
|
QPixmap p(QStringLiteral(":/style/landscape-mode.svg"));
|
|
int h = ui->trackScrollBar->height();
|
|
QSize s(h, h);
|
|
ui->lbLandscape->setPixmap(p.scaled(s, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
|
ui->lbLandscape->setFixedSize(s);
|
|
}
|
|
|
|
updateEverything();
|
|
}
|
|
|
|
void ScriptTimeline::slotUnselectAll()
|
|
{
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (TrackWidget* tw : trackWidgetList)
|
|
tw->unselectAll();
|
|
}
|
|
|
|
int ScriptTimeline::getFilterTrackEntryRealTime(TrackEntry* te)
|
|
{
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (TrackWidget* tw : trackWidgetList)
|
|
{
|
|
for (FilterTrackWidget* ftw : tw->filterLines())
|
|
{
|
|
const QMap<int, TrackContentWidget*>& filterContentWidgets = ftw->fbw()->filterContentWidgets();
|
|
|
|
int foundMainTrackEntryId = -1;
|
|
for (auto itFcw = filterContentWidgets.begin(); itFcw != filterContentWidgets.end(); ++itFcw)
|
|
{
|
|
const TrackContentWidget* tcw = itFcw.value();
|
|
const QVector<TrackEntry*>& trackEntries = tcw->getTrackEntries();
|
|
|
|
for (const TrackEntry* tempFilterTe : trackEntries)
|
|
{
|
|
if (tempFilterTe->m_topicId == te->m_topicId)
|
|
{
|
|
foundMainTrackEntryId = itFcw.key();
|
|
break;
|
|
}
|
|
}
|
|
if (foundMainTrackEntryId != -1)
|
|
break;
|
|
}
|
|
|
|
if (foundMainTrackEntryId != -1)
|
|
{
|
|
const QVector<TrackEntry*>& trackEntries = tw->getTrackContentWidget()->getTrackEntries();
|
|
for (const TrackEntry* mainTe : trackEntries)
|
|
{
|
|
if (mainTe->m_topicId == foundMainTrackEntryId)
|
|
return mainTe->m_startTime + te->m_startTime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
QList<TrackWidget*> ScriptTimeline::getTrackWidgets() const
|
|
{
|
|
return ui->trackScrollArea->findChildren<TrackWidget*>();
|
|
}
|
|
|
|
TrackEntry* ScriptTimeline::getTrackEntryWithFiltersAsEntries(int topicId)
|
|
{
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (const TrackWidget* tw : trackWidgetList)
|
|
{
|
|
const QVector<TrackEntry*> trackEntries = tw->getTrackEntriesFiltersAsEntries();
|
|
for (TrackEntry* te : std::as_const(trackEntries))
|
|
{
|
|
if (te->m_topicId == topicId)
|
|
return te;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TrackEntry* ScriptTimeline::getTrackEntry(int topicId)
|
|
{
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (const TrackWidget* tw : trackWidgetList)
|
|
{
|
|
const QVector<TrackEntry*> trackEntries = tw->getTrackEntries();
|
|
for (TrackEntry* te : trackEntries)
|
|
{
|
|
if (te->m_topicId == topicId)
|
|
return te;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
QVector<TrackEntry*> ScriptTimeline::getTrackEntriesWithFiltersAsEntries(bool selectedEntriesOnly) const
|
|
{
|
|
QVector<TrackEntry*> tes;
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (TrackWidget* tw : trackWidgetList)
|
|
tes += tw->getTrackEntriesFiltersAsEntries(selectedEntriesOnly);
|
|
|
|
return tes;
|
|
}
|
|
|
|
QMap<QString, QVector<TrackEntry*>> ScriptTimeline::getTrackEntriesWithFilters(bool selectedEntriesOnly) const
|
|
{
|
|
QMap<QString, QVector<TrackEntry*>> trackList;
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (const TrackWidget* tw : trackWidgetList)
|
|
{
|
|
QString trackName(tw->trackName());
|
|
const QVector<TrackEntry*> trackEntries = tw->getTrackEntriesWithFilters(selectedEntriesOnly);
|
|
|
|
trackList.insert(trackName, trackEntries);
|
|
}
|
|
|
|
return trackList;
|
|
}
|
|
|
|
QVector<TrackEntry*> ScriptTimeline::getTrackEntries(bool selectedEntriesOnly) const
|
|
{
|
|
QVector<TrackEntry*> tes;
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (const TrackWidget* tw : trackWidgetList)
|
|
tes += tw->getTrackEntries(selectedEntriesOnly);
|
|
|
|
return tes;
|
|
}
|
|
|
|
bool ScriptTimeline::importTrackEntries(const QVector<LtsScriptTimeline::TrackEntry*>& importTes)
|
|
{
|
|
|
|
int importLength = 0;
|
|
for (TrackEntry* te : std::as_const(importTes))
|
|
{
|
|
int teLen = te->m_startTime + te->m_length;
|
|
if (teLen > importLength)
|
|
importLength = teLen;
|
|
}
|
|
|
|
const QVector<TrackEntry*> tes = getTrackEntries();
|
|
int pasteTime = m_time;
|
|
|
|
for (TrackEntry* te : std::as_const(tes))
|
|
{
|
|
int start = te->m_startTime;
|
|
int sum = start + te->m_length;
|
|
|
|
if (start < m_time && sum > m_time && pasteTime < sum)
|
|
{
|
|
pasteTime = sum;
|
|
}
|
|
}
|
|
|
|
int diff = pasteTime - m_time;
|
|
|
|
for (TrackEntry* te : std::as_const(tes))
|
|
{
|
|
if (te->m_startTime >= m_time)
|
|
te->m_startTime += importLength + diff;
|
|
}
|
|
|
|
QVector<LtsScriptTimeline::TrackEntry*> finalImportTes(importTes);
|
|
|
|
for (TrackEntry* te : finalImportTes)
|
|
{
|
|
|
|
te->m_startTime += pasteTime;
|
|
|
|
if (!addTopic(te))
|
|
{
|
|
qCritical() << "could not add te #" << te->m_topicId;
|
|
delete te;
|
|
}
|
|
}
|
|
|
|
m_time = pasteTime;
|
|
setIndicatorTime(m_time);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ScriptTimeline::addTopic(TrackEntry* te)
|
|
{
|
|
if (te->m_topicId == 0)
|
|
te->m_topicId = nextUniqueId();
|
|
|
|
if (te->m_channelName.isEmpty())
|
|
te->m_channelName = tr("Unnamed Track");
|
|
|
|
te->isSet = true;
|
|
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (TrackWidget* tw : trackWidgetList)
|
|
{
|
|
if (tw->trackName() == te->m_channelName)
|
|
{
|
|
m_ignoreChanges = true;
|
|
bool result = tw->addTrackEntry(te, false);
|
|
m_ignoreChanges = false;
|
|
|
|
if (!result)
|
|
return false;
|
|
|
|
tw->updateMe();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
QVector<TrackEntry*> ScriptTimeline::getSelectedEntriesWithFilterEntries() const
|
|
{
|
|
QVector<TrackEntry*> tes;
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
|
|
for (const TrackWidget* tw : std::as_const(tws))
|
|
tes += tw->getTrackEntriesFiltersAsEntries(true);
|
|
|
|
return tes;
|
|
}
|
|
|
|
QString ScriptTimeline::newTrackName() const
|
|
{
|
|
QString newTrackName;
|
|
int add = 1;
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
bool newTrackNameFound = false;
|
|
while (!newTrackNameFound)
|
|
{
|
|
newTrackNameFound = true;
|
|
newTrackName = tr("New Track %1").arg(add);
|
|
for (const TrackWidget* tw : trackWidgetList)
|
|
{
|
|
if (tw->trackName() == newTrackName)
|
|
{
|
|
|
|
add++;
|
|
newTrackNameFound = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return newTrackName;
|
|
}
|
|
|
|
void ScriptTimeline::updateAllTracks()
|
|
{
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
|
|
for (TrackWidget* tw : tws)
|
|
tw->updateMe();
|
|
}
|
|
|
|
QVector<TrackEntry*> ScriptTimeline::getSelectedFilterEntries() const
|
|
{
|
|
QVector<TrackEntry*> result;
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
for (const TrackWidget* tw : std::as_const(tws))
|
|
{
|
|
result += tw->getSelectedFilterEntries();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void ScriptTimeline::slotDeleteSelectedTopics()
|
|
{
|
|
int topicCount = 0;
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
for (const TrackWidget* tw : std::as_const(tws))
|
|
{
|
|
topicCount += tw->getSelectedFilterEntries().count();
|
|
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
topicCount += tcw->getSelectedEntries().count();
|
|
}
|
|
|
|
if (!topicCount)
|
|
{
|
|
QApplication::beep();
|
|
return;
|
|
}
|
|
|
|
connect(new LtsWidgets::LtsMessageDialogNew(
|
|
this, tr("Are you sure that you want to delete %1 topics?").arg(topicCount), QString(),
|
|
LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Yes | LtsWidgets::Button::No),
|
|
&LtsWidgets::LtsMessageDialogNew::yes, this, [this]() {
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
for (const TrackWidget* tw : std::as_const(tws))
|
|
{
|
|
|
|
const QVector<FilterTrackWidget*>& ftws = tw->filterLines();
|
|
|
|
for (const FilterTrackWidget* ftw : std::as_const(ftws))
|
|
{
|
|
const FilterBackgroundWidget* fbw = ftw->fbw();
|
|
const QMap<int, TrackContentWidget*>& fcws = fbw->filterContentWidgets();
|
|
|
|
for (TrackContentWidget* tcw : fcws)
|
|
tcw->deleteSelectedTopics();
|
|
}
|
|
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
tcw->deleteSelectedTopics();
|
|
}
|
|
});
|
|
}
|
|
|
|
void ScriptTimeline::slotCutTopics()
|
|
{
|
|
slotCopyTopics();
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
for (const TrackWidget* tw : std::as_const(tws))
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
tcw->deleteSelectedTopics();
|
|
}
|
|
}
|
|
|
|
void ScriptTimeline::slotCopyTopics()
|
|
{
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
int firstTeTime = -1;
|
|
|
|
for (const TrackWidget* tw : std::as_const(tws))
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
|
|
const QVector<TrackEntry*>& selected = tcw->getSelectedEntries();
|
|
|
|
if (tcw->getSelectedEntries().isEmpty())
|
|
continue;
|
|
|
|
for (const TrackEntry* te : std::as_const(selected))
|
|
{
|
|
if (firstTeTime > te->m_startTime || firstTeTime == -1)
|
|
firstTeTime = te->m_startTime;
|
|
}
|
|
}
|
|
|
|
if (firstTeTime == -1)
|
|
{
|
|
QApplication::beep();
|
|
return;
|
|
}
|
|
|
|
m_copyTopics.clear();
|
|
|
|
for (TrackWidget* tw : tws)
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
|
|
if (tcw->getSelectedEntries().isEmpty())
|
|
continue;
|
|
|
|
connect(tw, &QObject::destroyed, this,
|
|
[this](QObject* obj) { m_copyTopics.remove(static_cast<TrackWidget*>(obj)); });
|
|
|
|
m_copyTopics.insert(tw,
|
|
TrackContentWidget::itemsToData(tw->getTrackEntriesWithFilters(true), firstTeTime, false));
|
|
}
|
|
}
|
|
|
|
void ScriptTimeline::slotPasteTopics()
|
|
{
|
|
if (m_copyTopics.isEmpty() || m_time < 0)
|
|
{
|
|
QApplication::beep();
|
|
return;
|
|
}
|
|
|
|
for (auto i = m_copyTopics.constBegin(); i != m_copyTopics.constEnd(); ++i)
|
|
{
|
|
const TrackWidget* tw = i.key();
|
|
const QByteArray& data = i.value();
|
|
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
int dummy = 0;
|
|
const TemporaryPointerVector<TrackEntry*> tempTes(TrackContentWidget::dataToItems(data, dummy));
|
|
if (!tcw->canPasteTopics(tempTes, 0, m_time))
|
|
{
|
|
new LtsWidgets::LtsMessageDialogNew(
|
|
this, tr("Topics can't be pasted here as one or more topics intersects with another existing topic."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (auto i = m_copyTopics.constBegin(); i != m_copyTopics.constEnd(); ++i)
|
|
{
|
|
TrackWidget* tw = i.key();
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
const QByteArray& data = i.value();
|
|
|
|
int dummy = 0;
|
|
const TemporaryPointerVector<TrackEntry*> tempTes(TrackContentWidget::dataToItems(data, dummy));
|
|
tcw->forcePasteTopics(tempTes, 0, m_time);
|
|
}
|
|
}
|
|
|
|
void ScriptTimeline::slotMoveAll(int direction)
|
|
{
|
|
int factor = 1;
|
|
|
|
if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier))
|
|
factor = 10;
|
|
|
|
QList<TrackWidget*> tws = getTrackWidgets();
|
|
|
|
for (TrackWidget* tw : std::as_const(tws))
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
if (!tcw->canMoveAll(direction * factor))
|
|
{
|
|
QApplication::beep();
|
|
qWarning() << "cols and/or parls @" << tcw;
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (TrackWidget* tw : std::as_const(tws))
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
tcw->forceMoveAll(direction * factor);
|
|
}
|
|
}
|
|
|
|
static inline bool containsFailedTrackContentWidget(const QHash<TrackContentWidget*, bool>& mlmsTCWStates)
|
|
{
|
|
auto end = mlmsTCWStates.end();
|
|
for (auto it = mlmsTCWStates.begin(); it != end; ++it)
|
|
{
|
|
if (it.value() == false)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TrackWidget* ScriptTimeline::addTrack(const QString& name, bool filtersAllowed)
|
|
{
|
|
TrackWidget* tw = new TrackWidget(m_baseTrackWidgetWidth, m_readonly, m_tools, filtersAllowed);
|
|
tw->setScriptTimeline(this);
|
|
tw->setTrackName(name);
|
|
|
|
ui->trackLayout->insertWidget(1, tw);
|
|
|
|
connect(tw, &TrackWidget::signalTrackEntrySelectionChanged, this, &ScriptTimeline::slotTrackEntrySelectionChanged);
|
|
connect(tw, &TrackWidget::signalTrackEntryDeleted, this, &ScriptTimeline::signalTrackEntryDeleted);
|
|
connect(tw, &TrackWidget::signalUnselectAll, this, &ScriptTimeline::slotUnselectAll);
|
|
connect(tw, &TrackWidget::signalSelectTrack, this, &ScriptTimeline::slotSelectTrack);
|
|
connect(tw, &TrackWidget::signalChangesMade, this, &ScriptTimeline::signalChangesMade);
|
|
connect(tw, &TrackWidget::signalEditEntry, this, &ScriptTimeline::signalEditEntry);
|
|
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
|
|
connect(tcw, &TrackContentWidget::signalStartRubberband, this, &ScriptTimeline::slotStartRubberband);
|
|
connect(tcw, &TrackContentWidget::signalStopRubberband, this, &ScriptTimeline::slotStopRubberband);
|
|
connect(tcw, &TrackContentWidget::signalEscapeRubberband, this, &ScriptTimeline::slotEscapeRubberband);
|
|
connect(tcw, &TrackContentWidget::signalModifyRubberband, this, &ScriptTimeline::slotModifyRubberband);
|
|
connect(tcw, &TrackContentWidget::signalSelectAllGlobal, this, &ScriptTimeline::selectAll);
|
|
connect(tcw, &TrackContentWidget::signalCopyTopics, this, &ScriptTimeline::slotCopyTopics);
|
|
connect(tcw, &TrackContentWidget::signalPasteTopics, this, &ScriptTimeline::slotPasteTopics);
|
|
connect(tcw, &TrackContentWidget::signalCutTopics, this, &ScriptTimeline::slotCutTopics);
|
|
connect(tcw, &TrackContentWidget::signalDeleteSelectedTopics, this, &ScriptTimeline::slotDeleteSelectedTopics);
|
|
connect(tcw, &TrackContentWidget::signalSave, this, &ScriptTimeline::signalSave);
|
|
connect(tcw, &TrackContentWidget::signalMoveAll, this, &ScriptTimeline::slotMoveAll);
|
|
connect(tcw, &TrackContentWidget::signalScroll, this, &ScriptTimeline::slotScroll);
|
|
|
|
connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionFailed, this, [this]() {
|
|
TrackContentWidget* senderTcw = qobject_cast<TrackContentWidget*>(sender());
|
|
|
|
if (senderTcw == nullptr)
|
|
return;
|
|
|
|
m_mlmsTCWStates.insert(senderTcw, false);
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
|
|
for (TrackWidget* tw : tws)
|
|
{
|
|
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
tcw->setDragPossible(false);
|
|
}
|
|
});
|
|
|
|
connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionOkay, this, [this]() {
|
|
TrackContentWidget* senderTcw = qobject_cast<TrackContentWidget*>(sender());
|
|
|
|
if (senderTcw == nullptr)
|
|
return;
|
|
|
|
m_mlmsTCWStates.insert(senderTcw, true);
|
|
|
|
if (containsFailedTrackContentWidget(m_mlmsTCWStates))
|
|
return;
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
|
|
for (TrackWidget* tw : tws)
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
|
|
if (tcw == senderTcw)
|
|
continue;
|
|
|
|
tcw->setDragPossible(true);
|
|
}
|
|
});
|
|
|
|
connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionDone, this, [this](int dropPos) {
|
|
TrackContentWidget* senderTcw = qobject_cast<TrackContentWidget*>(sender());
|
|
|
|
if (senderTcw == nullptr)
|
|
return;
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
|
|
for (TrackWidget* tw : tws)
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
|
|
if (tcw == senderTcw)
|
|
continue;
|
|
|
|
if (containsFailedTrackContentWidget(m_mlmsTCWStates) || dropPos == -1)
|
|
{
|
|
QApplication::beep();
|
|
tcw->dragEnd();
|
|
}
|
|
else
|
|
tcw->dragDrop(dropPos);
|
|
}
|
|
|
|
m_mlmsMaster = nullptr;
|
|
m_mlmsTCWStates.clear();
|
|
m_ignoreChanges = false;
|
|
|
|
updateAllTracks();
|
|
});
|
|
|
|
connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionMove, this, [this](int x) {
|
|
TrackContentWidget* senderTcw = qobject_cast<TrackContentWidget*>(sender());
|
|
|
|
if (senderTcw == nullptr)
|
|
return;
|
|
|
|
if (senderTcw != m_mlmsMaster)
|
|
return;
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
|
|
for (TrackWidget* tw : tws)
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
|
|
if (tcw == senderTcw)
|
|
continue;
|
|
|
|
tcw->dragMove(x);
|
|
}
|
|
});
|
|
|
|
connect(tcw, &TrackContentWidget::signalMultiLineMultiSelectionStart, this, [this](int dragPosTime) {
|
|
if (m_mlmsMaster != nullptr)
|
|
return;
|
|
|
|
TrackContentWidget* senderTcw = qobject_cast<TrackContentWidget*>(sender());
|
|
|
|
if (senderTcw == nullptr)
|
|
return;
|
|
|
|
m_mlmsMaster = senderTcw;
|
|
m_ignoreChanges = true;
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
|
|
for (TrackWidget* tw : tws)
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
|
|
if (tcw == senderTcw)
|
|
continue;
|
|
|
|
tcw->dragEnter(tcw->selectedItemsToData(dragPosTime));
|
|
}
|
|
});
|
|
|
|
return tw;
|
|
}
|
|
|
|
void ScriptTimeline::slotEscapeRubberband()
|
|
{
|
|
if (m_rubberbandAction.rubberBand == nullptr)
|
|
return;
|
|
|
|
m_rubberbandAction.rubberBand->deleteLater();
|
|
}
|
|
|
|
void ScriptTimeline::slotStopRubberband()
|
|
{
|
|
if (m_rubberbandAction.rubberBand == nullptr)
|
|
return;
|
|
|
|
QRect geo(m_rubberbandAction.rubberBand->geometry());
|
|
|
|
m_rubberbandAction.rubberBand->deleteLater();
|
|
|
|
if (m_readonly)
|
|
return;
|
|
|
|
QPoint rubberbandLength = geo.topLeft() - geo.bottomRight();
|
|
if (rubberbandLength.manhattanLength() <= 6)
|
|
return;
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
for (TrackWidget* tw : tws)
|
|
{
|
|
TrackContentWidget* tcw = tw->getTrackContentWidget();
|
|
|
|
QRect tcwGeo(tcw->geometry());
|
|
QPoint topLeft(tcw->mapTo(this, tcwGeo.topLeft()));
|
|
QPoint bottomRight(tcw->mapTo(this, tcwGeo.bottomRight()));
|
|
|
|
if (QRect(topLeft, bottomRight).intersects(geo))
|
|
{
|
|
QPoint tcwTL(tcw->mapFrom(this, geo.topLeft()));
|
|
QPoint tcwBR(tcw->mapFrom(this, geo.bottomRight()));
|
|
|
|
int fromPos = tcwTL.x();
|
|
int toPos = tcwBR.x();
|
|
|
|
tcw->selectEntriesAtPositions(fromPos, toPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptTimeline::slotStartRubberband(const QPoint&)
|
|
{
|
|
if (m_readonly)
|
|
return;
|
|
|
|
if (m_rubberbandAction.rubberBand == nullptr)
|
|
{
|
|
QPoint cursorPos(mapFromGlobal(QCursor::pos()));
|
|
m_rubberbandAction.origin = cursorPos;
|
|
m_rubberbandAction.rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
|
|
|
|
m_rubberbandAction.rubberBand->setGeometry(QRect(cursorPos, QSize()));
|
|
m_rubberbandAction.rubberBand->show();
|
|
}
|
|
}
|
|
|
|
void ScriptTimeline::slotModifyRubberband()
|
|
{
|
|
if (!m_rubberbandAction.rubberBand)
|
|
return;
|
|
|
|
m_rubberbandAction.rubberBand->setGeometry(
|
|
QRect(m_rubberbandAction.origin, mapFromGlobal(QCursor::pos())).normalized());
|
|
}
|
|
|
|
void ScriptTimeline::slotTrackEntrySelectionChanged(TrackEntry*)
|
|
{
|
|
if (m_ignoreChanges)
|
|
return;
|
|
|
|
const QList<TrackWidget*> tws = getTrackWidgets();
|
|
|
|
bool hasSelections = false;
|
|
bool activateMultiLineMultiSelectionMode = false;
|
|
for (const TrackWidget* tw : tws)
|
|
{
|
|
if (!hasSelections)
|
|
{
|
|
if (!tw->getSelectedEntries().isEmpty() || !tw->getSelectedFilterEntries().isEmpty())
|
|
hasSelections = true;
|
|
}
|
|
else if (!tw->getSelectedEntries().isEmpty())
|
|
{
|
|
|
|
activateMultiLineMultiSelectionMode = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (TrackWidget* tw : std::as_const(tws))
|
|
tw->getTrackContentWidget()->setMultiLineMultiSelectionMode(activateMultiLineMultiSelectionMode);
|
|
|
|
emit signalTrackEntrySelectionChanged(nullptr);
|
|
}
|
|
|
|
void ScriptTimeline::slotSelectTrack()
|
|
{
|
|
const QList<TrackWidget*> trackWidgetList = getTrackWidgets();
|
|
for (const TrackWidget* tw : trackWidgetList)
|
|
{
|
|
if (tw->isSelected())
|
|
{
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScriptTimeline::updateEverything()
|
|
{
|
|
|
|
TrackWidget* tw = ui->trackScrollArea->findChild<TrackWidget*>();
|
|
|
|
if (tw != nullptr)
|
|
m_tools->m_paintWidth = tw->width();
|
|
|
|
ui->indicator->updateMe();
|
|
|
|
updateAllTracks();
|
|
update();
|
|
}
|
|
|
|
void ScriptTimeline::slotScrollbarValueChanged()
|
|
{
|
|
m_tools->m_scrollPos = ui->trackScrollBar->value();
|
|
|
|
updateEverything();
|
|
}
|
|
} // namespace LtsScriptTimeline
|