- training_area_telegrams.cpp:3168: Fixed QJsonObject initializer list syntax
- Changed QJsonObject({{...}}) to QJsonObject{{...}} (double braces)
- Changed header.toJson() to header().toJson() (method call)
- track_content_widget.cpp:37,51: Fixed QJsonDocument serialization
- Replaced toBinaryData() with toJson(QJsonDocument::Compact)
- Replaced fromBinaryData() with fromJson()
- Qt6 removed binary serialization methods
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1431 lines
39 KiB
C++
1431 lines
39 KiB
C++
#include "track_content_widget.h"
|
|
|
|
#include "filter_background_widget.h"
|
|
#include "script_timeline.h"
|
|
#include "temporary_pointer_vector.h"
|
|
#include "tools.h"
|
|
#include "track_drag_handler.h"
|
|
#include "track_entry.h"
|
|
#include "track_entry_painters.h"
|
|
#include "track_resize_handler.h"
|
|
#include "track_select_handler.h"
|
|
#include "track_widget.h"
|
|
|
|
#include <LtsWidgets/lts_message_dialog.h>
|
|
#include <QApplication>
|
|
#include <QBrush>
|
|
#include <QByteArray>
|
|
#include <QColor>
|
|
#include <QDataStream>
|
|
#include <QGuiApplication>
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QJsonParseError>
|
|
#include <QJsonValue>
|
|
#include <QKeyEvent>
|
|
#include <QPainter>
|
|
#include <QtCore/qglobal.h>
|
|
#include <cassert>
|
|
#include <utility>
|
|
|
|
namespace LtsScriptTimeline
|
|
{
|
|
|
|
QDataStream& operator<<(QDataStream& ds, const TrackEntry& te)
|
|
{
|
|
const QByteArray jsonContent = QJsonDocument(te.m_detailData).toJson(QJsonDocument::Compact);
|
|
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::fromJson(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 : std::as_const(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 : std::as_const(_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 : std::as_const(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 : std::as_const(previewCopy))
|
|
{
|
|
|
|
int startOffset_s = previewEntry->m_startTime;
|
|
|
|
if (!hack)
|
|
startOffset_s -= startTimeMostLeft_s;
|
|
|
|
for (TrackEntry* pTrackEntry : std::as_const(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 : std::as_const(previewCopy))
|
|
{
|
|
correspondingDifferentTopicTypes.clear();
|
|
|
|
int startOffset_s = previewEntry->m_startTime;
|
|
|
|
if (!hack)
|
|
startOffset_s -= startTimeMostLeft_s;
|
|
|
|
for (const QPair<TrackEntryType, TrackEntryType>& pair : std::as_const(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 : std::as_const(correspondingDifferentTopicTypes))
|
|
{
|
|
|
|
for (TrackEntry* pTrackEntry : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(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 : std::as_const(tes))
|
|
{
|
|
bool found = false;
|
|
|
|
for (TrackEntry* thisTe : std::as_const(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 : std::as_const(m_entryList))
|
|
{
|
|
if (pPreview->m_topicId == pTE->m_topicId)
|
|
{
|
|
hasJustBeenAddedByUser = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const auto& tcw : std::as_const(m_tcwCorresponding))
|
|
{
|
|
for (auto pTE : std::as_const(tcw->m_entryList))
|
|
{
|
|
if (pPreview->m_topicId == pTE->m_topicId)
|
|
{
|
|
hasJustBeenAddedByUser = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasJustBeenAddedByUser)
|
|
{
|
|
|
|
m_selected.clear();
|
|
|
|
for (const auto& tcw : std::as_const(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 : std::as_const(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 : std::as_const(m_tcwCorresponding))
|
|
tcw->removeTrackEntryByTopicId(prevTe);
|
|
|
|
m_unselectAllImmune = false;
|
|
}
|
|
|
|
dragEnd();
|
|
|
|
if (m_isMultiLineMultiSelectionMode)
|
|
{
|
|
|
|
for (TrackEntry* oldSel : std::as_const(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 : std::as_const(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();
|
|
}
|
|
|
|
} // namespace LtsScriptTimeline
|