Files
qt6-migration-pipeline/Lts/Common/LtsScriptTimeline/track_content_widget.cpp
T

1653 lines
36 KiB
C++
Executable File

#include "track_content_widget.h"
#include "track_entry.h"
#include "tools.h"
#include "track_entry_painters.h"
#include "track_widget.h"
#include "script_timeline.h"
#include "filter_background_widget.h"
#include "track_drag_handler.h"
#include "track_select_handler.h"
#include "track_resize_handler.h"
#include "temporary_pointer_vector.h"
#include <LtsWidgets/lts_message_dialog.h>
#include <QPainter>
#include <QBrush>
#include <QColor>
#include <QByteArray>
#include <QDataStream>
#include <QKeyEvent>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonParseError>
#include <QJsonValue>
#include <QGuiApplication>
#include <QApplication>
#include <cassert>
#include <QtCore/qglobal.h>
namespace LtsScriptTimeline
{
QDataStream &operator<<(QDataStream &ds, const TrackEntry &te)
{
const QByteArray jsonContent = QJsonDocument(te.m_detailData).toBinaryData();
ds << te.m_topicId
<< te.m_startTime
<< te.m_length
<< static_cast<quint8>(te.m_type)
<< te.isSet
<< te.index
<< jsonContent
<< te.x << te.y << te.h << te.w
<< te.m_channelName
<< te.m_description;
return ds;
}
QDataStream &operator>>(QDataStream &ds, TrackEntry &te)
{
QByteArray jsonContent;
quint8 type;
ds >> te.m_topicId
>> te.m_startTime
>> te.m_length
>> type
>> te.isSet
>> te.index
>> jsonContent
>> te.x >> te.y >> te.h >> te.w
>> te.m_channelName
>> te.m_description;
te.m_type = static_cast<TrackEntryType>(type);
te.m_detailData = QJsonDocument::fromBinaryData(jsonContent).object();
return ds;
}
bool isCollision(int start_s, int end_s, int toCompare_start_s, int toCompare_end_s)
{
if (start_s >= toCompare_start_s &&
start_s < toCompare_end_s)
{
return true;
}
if (start_s < toCompare_start_s &&
end_s > toCompare_start_s)
{
return true;
}
return false;
}
bool isCollision2(int startPreview_s, int endPreview_s, const TrackEntry *pTrackEntry)
{
return isCollision(startPreview_s, endPreview_s, pTrackEntry->m_startTime, pTrackEntry->m_startTime + pTrackEntry->m_length);
}
void sortTrackEntriesChronologically(QVector<TrackEntry *> &vecTrackEntries)
{
std::sort(vecTrackEntries.begin(), vecTrackEntries.end(),
[](const TrackEntry *a, const TrackEntry *b) -> bool
{
return a->m_startTime < b->m_startTime;
});
}
bool TrackContentWidget::checkForCollisionsSimple(const TrackEntry &previewEntry) const
{
const int startPreview = previewEntry.m_startTime;
if (startPreview < 0)
return false;
const int endPreview = startPreview + previewEntry.m_length;
const QVector<TrackEntry *> trackEntries = getTrackEntries();
for (const TrackEntry *checkTe : trackEntries)
{
if (checkTe->m_topicId == previewEntry.m_topicId)
continue;
if (LtsScriptTimeline::isCollision2(startPreview, endPreview, checkTe))
return false;
}
return true;
}
bool TrackContentWidget::checkForCollisions(const TrackContentWidget *tcw, const QVector<TrackEntry *> &preview, int dropTime_s, QVector<TrackEntry *> &vecCollisions, bool ignoreSelected, bool ignoreSelfCheck)
{
QVector<TrackEntry *> _preview = preview;
sortTrackEntriesChronologically(_preview);
vecCollisions.clear();
QVector<TrackEntry *> vecEntriesToCheck;
const QVector<TrackEntry *> &trackEntries = tcw->getTrackEntries();
if (ignoreSelected)
{
const QVector<TrackEntry *> &selectedEntries = tcw->getSelectedEntries();
for (TrackEntry *te : qAsConst(trackEntries))
{
if (!selectedEntries.contains(te))
vecEntriesToCheck.append(te);
}
}
else
vecEntriesToCheck = trackEntries;
auto leftMostI = std::min_element(_preview.constBegin(), _preview.constEnd(), [](const TrackEntry * a, const TrackEntry * b)
{
return a->m_startTime < b->m_startTime;
});
if (leftMostI == _preview.constEnd())
return false;
int startTimeMostLeft_s = (*leftMostI)->m_startTime;
int startOffset_s = dropTime_s - startTimeMostLeft_s;
for (TrackEntry *previewEntry : qAsConst(_preview))
{
int startPreview_s = previewEntry->m_startTime + startOffset_s;
if (startPreview_s < 0)
{
vecCollisions.append(previewEntry);
continue;
}
int endPreview_s = startPreview_s + previewEntry->m_length;
for (const TrackEntry *checkTe : qAsConst(vecEntriesToCheck))
{
if (checkTe->m_topicId == previewEntry->m_topicId && !ignoreSelfCheck)
{
continue;
}
if (LtsScriptTimeline::isCollision2(startPreview_s, endPreview_s, checkTe))
{
vecCollisions.append(previewEntry);
break;
}
}
}
return !vecCollisions.isEmpty();
}
bool TrackContentWidget::checkForCollisions(const TrackContentWidget *pTCW, const QVector<TrackEntry *> &vecPreviewTrackEntries, int dropTime_s)
{
QVector<TrackEntry *> dummy_vecCollidingTopics;
return TrackContentWidget::checkForCollisions(pTCW, vecPreviewTrackEntries, dropTime_s, dummy_vecCollidingTopics, true);
}
QString TrackContentWidget::parallelError()
{
return tr("At least one topic of type Situation, Building, Audio or Video has been selected\nwhich can not be played parallel on both screens. Placement not possible.");
}
bool TrackContentWidget::checkForParallelTopics(const TrackContentWidget *tcw, const QVector<TrackEntry *> &preview, int dropTime_s, QVector<TrackEntry *> &vecParallelTopics, bool ignoreSelectedTopics, bool hack)
{
{
QVector<TrackEntryType> vecParallelForbiddenFilter = { TrackEntryType::Building, TrackEntryType::Audio, TrackEntryType::Video, TrackEntryType::SituationMap, TrackEntryType::Situation };
QVector<TrackEntry *> previewCopy(preview);
sortTrackEntriesChronologically(previewCopy);
QVector<TrackEntry *> vecTrackEntries(tcw->getTrackEntries());
auto itE = std::remove_if(previewCopy.begin(), previewCopy.end(),
[vecParallelForbiddenFilter](TrackEntry *pTE)
{
return !vecParallelForbiddenFilter.contains(pTE->m_type);
});
previewCopy.erase(itE, previewCopy.end());
if (ignoreSelectedTopics)
{
itE = std::remove_if(previewCopy.begin(), previewCopy.end(),
[tcw](TrackEntry *pTE)
{
return tcw->getSelectedEntries().contains(pTE);
});
previewCopy.erase(itE, previewCopy.end());
}
itE = std::remove_if(vecTrackEntries.begin(), vecTrackEntries.end(),
[vecParallelForbiddenFilter](TrackEntry *pTE)
{
return !vecParallelForbiddenFilter.contains(pTE->m_type);
});
vecTrackEntries.erase(itE, vecTrackEntries.end());
itE = std::remove_if(vecTrackEntries.begin(), vecTrackEntries.end(),
[tcw](TrackEntry *pTE)
{
return tcw->getSelectedEntries().contains(pTE);
});
vecTrackEntries.erase(itE, vecTrackEntries.end());
if (!previewCopy.isEmpty() && !vecTrackEntries.isEmpty())
{
auto leftMostI = std::min_element(previewCopy.constBegin(), previewCopy.constEnd(), [](const TrackEntry *a, const TrackEntry *b)
{
return a->m_startTime < b->m_startTime;
});
if (leftMostI != previewCopy.constEnd())
{
int startTimeMostLeft_s = (*leftMostI)->m_startTime;
for (TrackEntry *previewEntry : qAsConst(previewCopy))
{
int startOffset_s = previewEntry->m_startTime;
if (!hack)
startOffset_s -= startTimeMostLeft_s;
for (TrackEntry *pTrackEntry : qAsConst(vecTrackEntries))
{
if (previewEntry->m_type != pTrackEntry->m_type)
{
continue;
}
if (previewEntry->m_topicId == pTrackEntry->m_topicId)
{
continue;
}
int startPreview_s = startOffset_s;
if (!hack)
startPreview_s += dropTime_s;
int endPreview_s = startPreview_s + previewEntry->m_length;
if (LtsScriptTimeline::isCollision2(startPreview_s, endPreview_s, pTrackEntry))
{
vecParallelTopics.append(previewEntry);
break;
}
}
}
}
}
else
{
}
}
{
QVector<TrackEntry *> previewCopy(preview);
sortTrackEntriesChronologically(previewCopy);
QVector<QPair<TrackEntryType, TrackEntryType> > differentParallelForbidden =
{
{ TrackEntryType::Situation, TrackEntryType::Snapshot },
{ TrackEntryType::Situation, TrackEntryType::SituationReplay },
{ TrackEntryType::Situation, TrackEntryType::SituationMap },
};
QVector<TrackEntry *> vecTrackEntries(tcw->getTrackEntries());
int step = 1;
auto findUnusedType = [differentParallelForbidden, &step](const TrackEntry *pTE)
{
auto x = std::find_if(differentParallelForbidden.constBegin(), differentParallelForbidden.constEnd(), [pTE, step](const QPair<TrackEntryType, TrackEntryType> &pair)
{
bool x = (pair.first == pTE->m_type || pair.second == pTE->m_type);
if (x)
qDebug() << ">>"<<step<<".1 type" << (int)pTE->m_type << "in type list - not removing";
return x;
});
if (x == differentParallelForbidden.constEnd())
qDebug() << ">>"<<step<<".2 type" << (int)pTE->m_type << "not found in type list - removing!";
return (x == differentParallelForbidden.constEnd());
};
auto itE = std::remove_if(previewCopy.begin(), previewCopy.end(), findUnusedType);
previewCopy.erase(itE, previewCopy.end());
step = 2;
itE = std::remove_if(vecTrackEntries.begin(), vecTrackEntries.end(), findUnusedType);
vecTrackEntries.erase(itE, vecTrackEntries.end());
if (ignoreSelectedTopics)
{
itE = std::remove_if(previewCopy.begin(), previewCopy.end(),
[tcw](TrackEntry *pTE)
{
return tcw->getSelectedEntries().contains(pTE);
});
previewCopy.erase(itE, previewCopy.end());
}
itE = std::remove_if(vecTrackEntries.begin(), vecTrackEntries.end(),
[tcw](TrackEntry *pTE)
{
bool x = tcw->getSelectedEntries().contains(pTE);
return x;
});
vecTrackEntries.erase(itE, vecTrackEntries.end());
if (!previewCopy.size() || !vecTrackEntries.size())
{
return !vecParallelTopics.isEmpty();
}
auto leftMostI = std::min_element(previewCopy.constBegin(), previewCopy.constEnd(), [](const TrackEntry * a, const TrackEntry * b)
{
return a->m_startTime < b->m_startTime;
});
if (leftMostI == previewCopy.constEnd())
return !vecParallelTopics.isEmpty();
int startTimeMostLeft_s = (*leftMostI)->m_startTime;
QVector<TrackEntryType> correspondingDifferentTopicTypes;
for (TrackEntry *previewEntry : qAsConst(previewCopy))
{
correspondingDifferentTopicTypes.clear();
int startOffset_s = previewEntry->m_startTime;
if (!hack)
startOffset_s -= startTimeMostLeft_s;
for (const QPair<TrackEntryType, TrackEntryType> &pair : qAsConst(differentParallelForbidden))
{
if (pair.first == previewEntry->m_type)
correspondingDifferentTopicTypes += pair.second;
if (pair.second == previewEntry->m_type)
correspondingDifferentTopicTypes += pair.first;
}
Q_ASSERT(!correspondingDifferentTopicTypes.isEmpty());
if (correspondingDifferentTopicTypes.isEmpty())
continue;
for (const TrackEntryType correspondingDifferentTopicType : qAsConst(correspondingDifferentTopicTypes))
{
for (TrackEntry *pTrackEntry : qAsConst(vecTrackEntries))
{
if (pTrackEntry->m_type != correspondingDifferentTopicType)
{
continue;
}
if (previewEntry->m_topicId == pTrackEntry->m_topicId)
{
continue;
}
int startPreview_s = startOffset_s;
if (!hack)
startPreview_s += dropTime_s;
int endPreview_s = startPreview_s + previewEntry->m_length;
if (LtsScriptTimeline::isCollision2(startPreview_s, endPreview_s, pTrackEntry))
{
vecParallelTopics.append(previewEntry);
break;
}
}
}
}
}
return !vecParallelTopics.isEmpty();
}
bool TrackContentWidget::checkForParallelTopics(const TrackContentWidget *pTCW, const QVector<TrackEntry *> &vecPreviewTrackEntries, int dropTime_s, bool hack)
{
QVector<TrackEntry *> dummy_vecParallelTopics;
return checkForParallelTopics(pTCW, vecPreviewTrackEntries, dropTime_s, dummy_vecParallelTopics, false, hack);
}
QString TrackContentWidget::mime = QStringLiteral("application/rheinmetall.script.topic");
TrackContentWidget::TrackContentWidget(QWidget *parent, bool isFilter, Tools *tools, int mainTopicId)
: QWidget(parent)
, m_selectHandler(new SelectHandler(this))
, m_resizeHandler(new ResizeHandler(this))
, m_dragHandler(new DragHandler(this))
, m_dragOffsetTime(-1)
, m_dragPos(-1)
, m_mainSelected(nullptr)
, m_width(0)
, m_cursorSet(false)
, m_oldEntryLength(-1)
, m_unselectAllImmune(false)
, m_indicatorTime(-1)
, m_isFilter(isFilter)
, m_readonly(false)
, m_trackWidget(nullptr)
, m_scriptTimeline(nullptr)
, m_tools(tools)
, m_isMultiLineMultiSelectionMode(false)
, m_multiLineMultiSelectionOkay(false)
, m_dragPossible(false)
, m_moveValid(false)
, m_mainTopicId(mainTopicId)
{
setAcceptDrops(true);
setFocusPolicy(Qt::StrongFocus);
setMouseTracking(true);
addFilterType(TrackEntryType::TacticalPlan);
addFilterType(TrackEntryType::SituationMapFilter);
if (m_isFilter)
{
addAllowedType(TrackEntryType::Text);
addAllowedType(TrackEntryType::Pdf);
addAllowedType(TrackEntryType::Image);
}
}
TrackContentWidget::~TrackContentWidget()
{
delete m_selectHandler;
delete m_resizeHandler;
delete m_dragHandler;
QVector<TrackEntry *> entryList = m_entryList;
for (TrackEntry *te : entryList)
{
disconnect(te, &TrackEntry::destroyed,
this, nullptr);
}
qDeleteAll(entryList);
}
bool TrackContentWidget::setDescriptionForSelected()
{
if (getSelectedEntries().count() > 1)
{
QApplication::beep();
return false;
}
m_selectHandler->slotSetDescription();
return true;
}
void TrackContentWidget::addCorrespondingTrackWidget(TrackContentWidget *pCorrespondingTrackWidget)
{
m_tcwCorresponding += pCorrespondingTrackWidget;
m_dragHandler->m_tcwCorresponding += pCorrespondingTrackWidget;
m_resizeHandler->m_tcwCorresponding += pCorrespondingTrackWidget;
}
void TrackContentWidget::setTools(Tools *tools)
{
m_tools = tools;
}
void TrackContentWidget::setTrackWidget(TrackWidget *trackWidget)
{
m_trackWidget = trackWidget;
}
void TrackContentWidget::setReadonly(bool readonly)
{
m_readonly = readonly;
}
void TrackContentWidget::addFilterType(const TrackEntryType &tet)
{
if (m_isFilter)
addAllowedType(tet);
else
addDisallowedType(tet);
}
void TrackContentWidget::setIndicatorTime(int time)
{
m_indicatorTime = time;
update();
}
void TrackContentWidget::mouseMoveEvent(QMouseEvent *event)
{
if (!m_resizeHandler->mouseMoveEvent(event))
m_dragHandler->mouseMoveEvent(event);
emit signalModifyRubberband();
}
TrackEntry *TrackContentWidget::getTrackEntryAt(int clickedTime)
{
for (auto i = m_entryList.begin(); i != m_entryList.end(); ++i)
{
TrackEntry *te = *i;
if (te->m_startTime <= clickedTime
&& (te->m_startTime + te->m_length) >= clickedTime)
return te;
}
return nullptr;
}
void TrackContentWidget::mousePressEvent(QMouseEvent *event)
{
if (m_readonly)
return;
m_selectHandler->mousePressEvent(event);
m_resizeHandler->mousePressEvent(event);
m_dragHandler->mousePressEvent(event);
if (!m_selected.isEmpty())
return;
if (event->buttons() != Qt::LeftButton)
return;
emit signalStartRubberband(event->pos());
}
void TrackContentWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
m_width = event->size().width();
}
void TrackContentWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (m_readonly)
return;
if (!m_resizeHandler->m_resizing)
m_selectHandler->mouseReleaseEvent(event);
m_resizeHandler->mouseReleaseEvent(event);
emit signalStopRubberband();
}
void TrackContentWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.save();
if (m_isFilter)
{
painter.setBrush(QGuiApplication::palette().window().color());
painter.drawRect(rect());
}
painter.setPen(QColor(36, 36, 36));
painter.drawLine(QPoint(0, 0), QPoint(0, height()));
if (m_isFilter)
painter.drawLine(QPoint(width() - 1, 0), QPoint(width() - 1, height()));
painter.setClipping(true);
painter.setClipRect(1, 1, width() - 1, height() - 1);
for (auto i = m_entryList.begin(); i != m_entryList.end(); ++i)
{
TrackEntry *te = *i;
int startPx = m_tools->getPos(te->m_startTime, m_isFilter);
int endPx = m_tools->getPos(te->m_startTime + te->m_length, m_isFilter);
if (endPx < 0 || startPx > width())
continue;
int heightPx = height() - 2;
int lengthPx = endPx - startPx;
painter.save();
painter.translate(startPx, 1);
bool isSelected = m_selected.contains(te);
QRect trackRect(0, 0, lengthPx, heightPx);
drawTrackEntry(te, painter, trackRect, isSelected);
if (isSelected)
{
}
painter.restore();
}
if (m_dragPos > -1 && !m_preview.isEmpty())
{
int firstTeTime = -1;
for (const TrackEntry *te : qAsConst(m_preview))
{
if (firstTeTime > te->m_startTime || firstTeTime == -1)
firstTeTime = te->m_startTime;
}
int drop = m_tools->getTime(m_dragPos, m_isFilter);
int finalDrop = drop - m_dragOffsetTime;
int diff = finalDrop - firstTeTime;
int heightPx = height() - 2;
for (auto i = m_preview.constBegin(); i != m_preview.constEnd(); ++i)
{
const TrackEntry *te = *i;
if (m_dragHandler->m_vecCollisions.contains(*i) || m_dragHandler->m_vecParallel.contains(*i) || !isDragPossible() || !m_moveValid)
painter.setBrush(QColor("red"));
else
painter.setBrush(QColor("lightgreen"));
int time = te->m_startTime + diff;
int startPx = m_tools->getPos(time, m_isFilter);
int endPx = m_tools->getPos(time + te->m_length, m_isFilter);
if (endPx < 0 || startPx > width())
continue;
int lengthPx = endPx - startPx;
painter.drawRect(startPx, 1, lengthPx, heightPx);
}
}
if (m_indicatorTime > -1)
{
int pos = m_tools->getPos(m_indicatorTime, m_isFilter);
if (pos > 0 && pos <= m_tools->m_paintWidth)
{
painter.setPen(QColor("cyan"));
painter.drawLine(pos, 0, pos, height());
}
}
painter.restore();
}
void TrackContentWidget::dragEnterEvent(QDragEnterEvent *event)
{
if (m_readonly)
return;
m_dragHandler->dragEnterEvent(event);
}
void TrackContentWidget::dragLeave()
{
}
void TrackContentWidget::dragLeaveEvent(QDragLeaveEvent *event)
{
if (m_readonly)
return;
m_dragHandler->dragLeaveEvent(event);
}
void TrackContentWidget::unselect()
{
m_unselectAllImmune = true;
emit signalUnselectAll();
m_unselectAllImmune = false;
}
void TrackContentWidget::forceLocalUnselect()
{
m_mainSelected = nullptr;
m_selected.clear();
}
void TrackContentWidget::selectAll()
{
m_selected.clear();
for (TrackEntry *te : qAsConst(m_entryList))
{
m_selected.append(te);
m_mainSelected = te;
}
emit signalTrackEntrySelectionChanged(nullptr);
update();
}
void TrackContentWidget::unselectAll()
{
if (!m_unselectAllImmune)
forceLocalUnselect();
else
{
if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier))
{
forceLocalUnselect();
emit signalTrackEntrySelectionChanged(nullptr);
}
}
update();
}
TrackEntry *TrackContentWidget::getParentTopicOfFilter(TrackEntry *filterTe) const
{
if (m_isFilter && m_trackWidget != nullptr)
{
int mainTopicId = m_trackWidget->getParentTopicIdOfFilter(filterTe);
TrackContentWidget *mainTcw = m_trackWidget->getTrackContentWidget();
const QVector<TrackEntry *> &teList = mainTcw->getTrackEntries();
for (auto i = teList.constBegin(); i != teList.constEnd(); ++i)
{
TrackEntry *mainTe = *i;
if (mainTe->m_topicId != mainTopicId)
continue;
return mainTe;
}
}
return nullptr;
}
bool TrackContentWidget::addTrackEntry(TrackEntry *te, bool selectTrackEntry)
{
if (!allowedByType(te))
{
qWarning() << "TrackContentWidget::addTrackEntry: topic #" << te->m_topicId << "type" << (int)te->m_type << "not allowed";
return false;
}
connect(te, &TrackEntry::destroyed,
this, [this]()
{
TrackEntry *o = static_cast<TrackEntry *>(sender());
m_entryList.removeAll(o);
m_selected.removeAll(o);
if (m_mainSelected == o)
{
m_mainSelected = nullptr;
emit signalTrackEntrySelectionChanged(nullptr);
}
});
te->m_channelName = m_trackName;
m_entryList.append(te);
TrackEntry *mainTe = getParentTopicOfFilter(te);
if (mainTe != nullptr)
{
if ((te->m_startTime + te->m_length) > mainTe->m_length)
te->m_length = mainTe->m_length - te->m_startTime;
te->m_filterParentChannel = mainTe->m_channelName;
}
if (selectTrackEntry && !m_readonly)
selectEntry(te);
emit signalTrackEntryAdded(te);
update();
return true;
}
void TrackContentWidget::removeTrackEntryByTopicId(TrackEntry *te)
{
auto itE = std::remove_if(m_entryList.begin(), m_entryList.end(),
[te](TrackEntry * pTE)
{
if (pTE->m_topicId == te->m_topicId)
{
pTE->deleteLater();
return true;
}
return false;
});
m_entryList.erase(itE, m_entryList.end());
itE = std::remove_if(m_selected.begin(), m_selected.end(),
[te](TrackEntry * pTE)
{
if (pTE->m_topicId == te->m_topicId)
{
pTE->deleteLater();
return true;
}
return false;
});
m_selected.erase(itE, m_selected.end());
}
void TrackContentWidget::dropEvent(QDropEvent *event)
{
if (m_readonly)
return;
m_dragHandler->dropEvent(event);
}
void TrackContentWidget::dragMoveEvent(QDragMoveEvent *event)
{
if (m_readonly)
return;
m_dragHandler->dragMoveEvent(event);
}
bool TrackContentWidget::canMoveAll(int offset) const
{
if (m_selected.isEmpty())
return true;
auto leftMostI = std::min_element(m_selected.constBegin(), m_selected.constEnd(), [](const TrackEntry * a, const TrackEntry * b)
{
return a->m_startTime < b->m_startTime;
});
if (leftMostI == m_selected.constEnd())
return false;
int startTimeMostLeft = (*leftMostI)->m_startTime;
QVector<TrackEntry *> cols;
int newTime = startTimeMostLeft + offset;
checkForCollisions(this, m_selected, newTime, cols, true);
QVector<TrackEntry *> parls;
for (const auto &tcw : qAsConst(m_tcwCorresponding))
checkForParallelTopics(tcw, m_selected, newTime, parls);
if (!cols.isEmpty() || !parls.isEmpty())
return false;
return true;
}
void TrackContentWidget::forceMoveAll(int offset)
{
for (auto i = m_selected.begin(); i != m_selected.end(); ++i)
{
TrackEntry *te = *i;
int newTime = te->m_startTime + offset;
if (newTime < 0)
newTime = 0;
te->m_startTime = newTime;
}
update();
emit signalChangesMade();
}
void TrackContentWidget::deleteSelectedTopics()
{
m_mainSelected = nullptr;
m_selectHandler->m_pressedEntry = nullptr;
bool somethingSeleted = false;
for (auto &pTopic : m_selected)
{
m_preview.removeAll(pTopic);
m_entryList.removeAll(pTopic);
emit signalTrackEntryDeleted(pTopic->m_topicId);
pTopic->deleteLater();
somethingSeleted = true;
}
if (somethingSeleted)
emit signalTrackEntrySelectionChanged(nullptr);
m_selected.clear();
emit signalChangesMade();
update();
}
void TrackContentWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
if (m_readonly)
return;
m_selectHandler->mouseDoubleClick(event);
}
void TrackContentWidget::keyPressEvent(QKeyEvent *event)
{
if (m_readonly)
return;
if (event->key() == Qt::Key_Delete)
{
emit signalDeleteSelectedTopics();
}
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_C)
{
emit signalCopyTopics();
}
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_V)
{
emit signalPasteTopics();
}
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_X)
{
emit signalCutTopics();
}
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_A)
{
emit signalSelectAllGlobal();
}
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_S)
{
emit signalSave();
}
else if (QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_D)
{
setDescriptionForSelected();
}
else
{
m_resizeHandler->keyPressEvent(event);
m_selectHandler->keyPressEvent(event);
m_dragHandler->keyPressEvent(event);
}
if (event->key() == Qt::Key_Escape)
{
emit signalEscapeRubberband();
emit signalMultiLineMultiSelectionDone(-1);
}
}
void TrackContentWidget::selectEntry(int topicId)
{
for (auto i = m_entryList.begin(); i != m_entryList.end(); ++i)
{
TrackEntry *te = *i;
if (te->m_topicId == topicId)
{
selectEntry((te));
return;
}
}
}
void TrackContentWidget::selectEntriesAtPositions(int fromPos, int toPos)
{
m_unselectAllImmune = true;
for (TrackEntry *te : qAsConst(m_entryList))
{
int start = m_tools->getPos(te->m_startTime);
int end = m_tools->getPos(te->m_startTime + te->m_length);
if (end >= fromPos && start <= toPos)
selectEntry(te);
}
m_unselectAllImmune = false;
}
void TrackContentWidget::selectEntry(TrackEntry *te)
{
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
if (!m_unselectAllImmune && !ctrl)
unselect();
if (te == nullptr)
return;
if (!m_selected.contains(te))
{
m_selected.append(te);
m_mainSelected = te;
emit signalTrackEntrySelectionChanged(nullptr);
}
else
{
m_selected.removeAll(te);
if (m_mainSelected == te)
m_mainSelected = nullptr;
emit signalTrackEntrySelectionChanged(nullptr);
}
update();
}
bool TrackContentWidget::canPasteTopics(const QVector<TrackEntry *> &topics, int offsetTime, int pasteTime) const
{
TemporaryPointerVector<TrackEntry *> newTes;
newTes.reserve(topics.size());
for (TrackEntry *te : qAsConst(topics))
{
TrackEntry *newTe = new TrackEntry(te);
newTe->m_startTime = (newTe->m_startTime - offsetTime + pasteTime);
newTes += newTe;
}
QVector<TrackEntry *> dummy_vecCollidingTopics;
bool parl = false;
for (const auto &tcw : qAsConst(m_tcwCorresponding))
{
parl = checkForParallelTopics(tcw, newTes, pasteTime, true );
if (parl)
break;
}
if (checkForCollisions(this, newTes, pasteTime, dummy_vecCollidingTopics, false, true) || parl)
return false;
return true;
}
void TrackContentWidget::forcePasteTopics(const QVector<TrackEntry *> &topics, int offsetTime, int pasteTime)
{
m_selected.clear();
for (TrackEntry *te : qAsConst(topics))
{
TrackEntry *newTe = new TrackEntry(te);
newTe->m_topicId = m_scriptTimeline->nextUniqueId();
newTe->m_startTime = (newTe->m_startTime - offsetTime + pasteTime);
newTe->isSet = true;
m_unselectAllImmune = true;
if (!m_trackWidget->addTrackEntry(newTe, false))
{
qWarning() << "TrackContentWidget::forcePasteTopics: topic #" << te->m_topicId << "type" << (int)te->m_type << "not allowed";
delete newTe;
delete te;
continue;
}
m_unselectAllImmune = false;
}
}
bool TrackContentWidget::allowedByType(const TrackEntry *te) const
{
if (m_disallowedTypes.contains(te->m_type))
return false;
if (m_allowedTypes.isEmpty())
return true;
return (m_allowedTypes.contains(te->m_type));
}
void TrackContentWidget::setTrackName(const QString &trackName)
{
m_trackName = trackName;
for (auto i = m_entryList.begin(); i != m_entryList.end(); ++i)
{
TrackEntry *te = *i;
te->m_channelName = m_trackName;
}
}
TrackEntry *TrackContentWidget::getTrackEntryOnPos(QPoint globalPos)
{
QPoint clickedPos = mapFromGlobal(globalPos);
int clickedPosY = clickedPos.y();
int clickedPosX = clickedPos.x();
if (clickedPosY < 0 || clickedPosY > height())
return nullptr;
int clickedTime = m_tools->getTime(clickedPosX, m_isFilter);
return getTrackEntryAt(clickedTime);
}
QVector<TrackEntry *> TrackContentWidget::dataToItems(const QByteArray &data, int &offset)
{
if (data.isEmpty())
return {};
QDataStream dataStream(data);
int count;
dataStream >> count;
dataStream >> offset;
QVector<TrackEntry *> tes;
tes.reserve(count);
for (int i = 0; i < count; i++)
{
TrackEntry *te = new TrackEntry;
dataStream >> *te;
te->m_startTime -= offset;
tes += te;
}
return tes;
}
bool TrackContentWidget::dragEnter(const QByteArray &itemData)
{
dragEnd();
TemporaryPointerVector<TrackEntry *> tes(dataToItems(itemData, m_dragOffsetTime));
if (tes.isEmpty())
return false;
for (TrackEntry *te : qAsConst(tes))
{
if (!allowedByType(te))
{
qWarning() << "TrackContentWidget::dragEnter: topic #" << te->m_topicId << "type" << (int)te->m_type << "not allowed";
return false;
}
}
if (m_isFilter && m_mainTopicId != -1)
{
auto it = std::find_if(tes.begin(), tes.end(), [this](const TrackEntry * a)
{
return (a->m_topicId == m_mainTopicId);
});
if (it != tes.end())
return false;
}
const QVector<TrackEntry *> &thisTes = getTrackEntries();
if (m_isMultiLineMultiSelectionMode)
{
for (TrackEntry *te : qAsConst(tes))
{
bool found = false;
for (TrackEntry *thisTe : qAsConst(thisTes))
{
if (thisTe->m_topicId == te->m_topicId)
{
found = true;
break;
}
}
if (!found)
return false;
}
}
sortTrackEntriesChronologically(tes);
if (!m_isFilter && tes.size() == 1)
{
TrackEntry *pPreview = tes.front();
bool hasJustBeenAddedByUser = true;
for (auto pTE : qAsConst(m_entryList))
{
if (pPreview->m_topicId == pTE->m_topicId)
{
hasJustBeenAddedByUser = false;
break;
}
}
for (const auto &tcw : qAsConst(m_tcwCorresponding))
{
for (auto pTE : qAsConst(tcw->m_entryList))
{
if (pPreview->m_topicId == pTE->m_topicId)
{
hasJustBeenAddedByUser = false;
break;
}
}
}
if (hasJustBeenAddedByUser)
{
m_selected.clear();
for (const auto &tcw : qAsConst(m_tcwCorresponding))
tcw->m_selected.clear();
}
}
tes.setDiscardable(false);
m_preview = tes;
return true;
}
void TrackContentWidget::clearDrag()
{
qDeleteAll(m_preview);
m_preview.clear();
m_dragOffsetTime = -1;
m_dragPos = -1;
m_dragPossible = false;
m_moveValid = false;
update();
}
int TrackContentWidget::getDropPos() const
{
return (m_dragHandler == nullptr) ? -1 : m_dragHandler->m_dropPos;
}
void TrackContentWidget::dragEnd()
{
clearDrag();
}
bool TrackContentWidget::dragDrop(int pos)
{
if (!isDragPossible() || !m_moveValid)
{
dragEnd();
return false;
}
int dropTime = m_tools->getTime(pos, m_isFilter) - m_dragOffsetTime;
QVector<TrackEntry *> selectedOld = m_selected;
if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && !m_isMultiLineMultiSelectionMode)
unselect();
int firstTeTime = -1;
for (const TrackEntry *te : qAsConst(m_preview))
{
if (firstTeTime > te->m_startTime || firstTeTime == -1)
firstTeTime = te->m_startTime;
}
int selected = firstTeTime;
int diff = dropTime - selected;
for (auto i = m_preview.constBegin(); i != m_preview.constEnd(); ++i)
{
TrackEntry *prevTe = *i;
TrackEntry *newTe = new TrackEntry(prevTe);
newTe->m_topicId = m_scriptTimeline->nextUniqueId();
newTe->m_startTime = prevTe->m_startTime + diff;
newTe->isSet = true;
m_unselectAllImmune = true;
if (m_isFilter)
{
addTrackEntry(newTe, true);
}
else
{
if (m_trackWidget == nullptr || !m_trackWidget->addTrackEntry(newTe, true))
{
delete newTe;
return false;
}
}
update();
for (const auto &tcw : qAsConst(m_tcwCorresponding))
tcw->removeTrackEntryByTopicId(prevTe);
m_unselectAllImmune = false;
}
dragEnd();
if (m_isMultiLineMultiSelectionMode)
{
for (TrackEntry *oldSel : qAsConst(selectedOld))
emit signalTrackEntryDeleted(oldSel->m_topicId);
qDeleteAll(selectedOld);
emit signalMultiLineMultiSelectionDone(pos);
}
emit signalChangesMade();
return true;
}
bool TrackContentWidget::dragMove(int pos)
{
m_dragPos = pos;
int dropTime_s = m_tools->getTime(m_dragPos, m_isFilter) - m_dragOffsetTime;
checkForCollisions(this, m_preview, dropTime_s, m_dragHandler->m_vecCollisions, true);
m_dragHandler->m_vecParallel.clear();
for (const auto &tcw : qAsConst(m_tcwCorresponding))
checkForParallelTopics(tcw, m_preview, dropTime_s, m_dragHandler->m_vecParallel);
m_moveValid = m_dragHandler->m_vecCollisions.isEmpty() && m_dragHandler->m_vecParallel.isEmpty();
if (!m_moveValid || !isDragPossible())
{
setCursor(Qt::ForbiddenCursor);
}
else
{
setCursor(Qt::DragMoveCursor);
}
update();
if (m_isMultiLineMultiSelectionMode)
{
if (m_moveValid)
emit signalMultiLineMultiSelectionOkay();
else
emit signalMultiLineMultiSelectionFailed();
}
static int scrollByPixels = 1;
if (pos >= (m_width - 60))
emit signalScroll(scrollByPixels);
else if (pos < 60)
emit signalScroll(-scrollByPixels);
return m_moveValid;
}
TrackEntry *TrackContentWidget::trackEntryAtPos(int x) const
{
int time = m_tools->getTime(x);
const QVector<TrackEntry *> tes = getTrackEntries();
for (TrackEntry *te : tes)
{
int start = te->m_startTime;
if (time >= start && time <= (start + te->m_length))
return te;
}
return nullptr;
}
QByteArray TrackContentWidget::itemsToData(const QVector<TrackEntry *> &items, int dragPosTime, bool calculateFirstTeTime)
{
int count = items.count();
if (!count)
return {};
QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << count;
if (dragPosTime != -1)
{
int firstTeTime = -1;
if (calculateFirstTeTime)
{
for (TrackEntry *te : items)
{
if (firstTeTime > te->m_startTime || firstTeTime == -1)
firstTeTime = te->m_startTime;
}
}
else
firstTeTime = 0;
int finalOffsetTime = dragPosTime - firstTeTime;
dataStream << finalOffsetTime;
}
else
dataStream << 0;
for (auto i = items.constBegin(); i != items.constEnd(); ++i)
{
TrackEntry *te = *i;
dataStream << *te;
}
return itemData;
}
QByteArray TrackContentWidget::selectedItemsToData(int dragPosTime, bool calculateFirstTeTime)
{
return itemsToData(m_selected, dragPosTime, calculateFirstTeTime);
}
void TrackContentWidget::setDragPossible(bool possible)
{
m_dragPossible = possible;
update();
}
}