1203 lines
27 KiB
C++
Executable File
1203 lines
27 KiB
C++
Executable File
#include "script_timeline.h"
|
|
#include "ui_script_timeline.h"
|
|
#include "track_widget.h"
|
|
#include "tools.h"
|
|
#include "track_entry.h"
|
|
#include "track_content_widget.h"
|
|
#include "filter_track_widget.h"
|
|
#include "filter_background_widget.h"
|
|
#include "temporary_pointer_vector.h"
|
|
|
|
#include <LtsWidgets/lts_message_dialog.h>
|
|
|
|
#include <QMessageBox>
|
|
#include <QPixmap>
|
|
#include <QTimer>
|
|
#include <QTime>
|
|
#include <QRubberBand>
|
|
#include <QCursor>
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
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 : qAsConst(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 : qAsConst(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 : qAsConst(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 : qAsConst(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 : qAsConst(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 : qAsConst(tws))
|
|
{
|
|
result += tw->getSelectedFilterEntries();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void ScriptTimeline::slotDeleteSelectedTopics()
|
|
{
|
|
int topicCount = 0;
|
|
|
|
const QList<TrackWidget *> tws = getTrackWidgets();
|
|
for (const TrackWidget *tw : qAsConst(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 : qAsConst(tws))
|
|
{
|
|
|
|
const QVector<FilterTrackWidget *> &ftws = tw->filterLines();
|
|
|
|
for (const FilterTrackWidget *ftw : qAsConst(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 : qAsConst(tws))
|
|
{
|
|
TrackContentWidget *tcw = tw->getTrackContentWidget();
|
|
tcw->deleteSelectedTopics();
|
|
}
|
|
}
|
|
|
|
|
|
void ScriptTimeline::slotCopyTopics()
|
|
{
|
|
const QList<TrackWidget *> tws = getTrackWidgets();
|
|
int firstTeTime = -1;
|
|
|
|
|
|
|
|
for (const TrackWidget *tw : qAsConst(tws))
|
|
{
|
|
TrackContentWidget *tcw = tw->getTrackContentWidget();
|
|
|
|
const QVector<TrackEntry *> &selected = tcw->getSelectedEntries();
|
|
|
|
if (tcw->getSelectedEntries().isEmpty())
|
|
continue;
|
|
|
|
for (const TrackEntry *te : qAsConst(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 : qAsConst(tws))
|
|
{
|
|
TrackContentWidget *tcw = tw->getTrackContentWidget();
|
|
if (!tcw->canMoveAll(direction * factor))
|
|
{
|
|
QApplication::beep();
|
|
qWarning() << "cols and/or parls @" << tcw;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
for (TrackWidget *tw : qAsConst(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 : qAsConst(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();
|
|
}
|
|
}
|