Files
qt6-migration-pipeline/Lts/Common/LtsTimeline/timeline_view_drag_handler.cpp
T

506 lines
13 KiB
C++
Executable File

#include "timeline_view_drag_handler.h"
#include "item.h"
#include "track.h"
#include <QApplication>
#include <QMouseEvent>
#include <QDrag>
#include <QMimeData>
#include <QDebug>
namespace LtsTimeline
{
TimelineViewDragHandler::TimelineViewDragHandler(TimelineView *view)
: m_view(view)
{}
TimelineViewDragHandler::~TimelineViewDragHandler()
{
m_view = nullptr;
}
void TimelineViewDragHandler::dragEnterEvent(QDragEnterEvent *e)
{
if (!m_view->timelineModel()->isEditable() || e->source() != m_view || m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid())
{
e->ignore();
return;
}
if (e->mimeData()->hasFormat(Item::mimetype))
e->acceptProposedAction();
else
e->ignore();
}
void TimelineViewDragHandler::dragMoveEvent(QDragMoveEvent *e)
{
if (!m_view->timelineModel()->isEditable() || e->source() != m_view || m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid())
{
e->ignore();
return;
}
if (!e->mimeData()->hasFormat(Item::mimetype))
return;
m_dragPos = e->pos();
m_view->m_header->setHoverPosition(m_dragPos.x());
e->setAccepted(canDragItemsTo(e->pos()));
m_view->viewport()->update();
}
void TimelineViewDragHandler::dragLeaveEvent(QDragLeaveEvent *e)
{
Q_UNUSED(e);
m_dragPos = {};
}
void TimelineViewDragHandler::dropEvent(QDropEvent *e)
{
if (!m_view->timelineModel()->isEditable() || e->source() != m_view || m_dragItemIds.isEmpty())
{
e->ignore();
return;
}
if (!e->mimeData()->hasFormat(Item::mimetype))
return;
if (!canDragItemsTo(e->pos()))
{
clear();
e->acceptProposedAction();
return;
}
qint64 dt = deltaTime();
try
{
for (const int &itemId : qAsConst(m_dragItemIds))
{
const Item &item = m_view->timelineModel()->item(itemId);
m_view->timelineModel()->moveItemToTrack(itemId, dragItemTrackId(item), item.startTime() + dt);
}
}
catch (const QString &str) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " " << str; }
catch (...) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed"; }
clear();
e->acceptProposedAction();
}
void TimelineViewDragHandler::mouseMoveEvent(QMouseEvent *e)
{
if (!m_view->timelineModel()->isEditable())
return;
if (e->buttons() != Qt::LeftButton)
{
clear();
return;
}
if (m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid())
return;
if ((e->pos() - m_dragStartPos).manhattanLength() < QApplication::startDragDistance())
return;
const QModelIndex index = m_view->indexAt(e->pos());
if (!index.isValid() || index.column() == Section::eActuator)
{
m_view->setCursor(QCursor(Qt::ForbiddenCursor));
return;
}
m_dragPos = e->pos();
m_view->setCursor(QCursor(canDragItemsTo(m_dragPos) ? Qt::ClosedHandCursor : Qt::ForbiddenCursor));
if (index.data().toInt() == m_dragStartIndex.data().toInt())
return;
QMimeData *mimeData = new QMimeData();
mimeData->setData(Item::mimetype, {});
QDrag *drag = new QDrag(m_view);
drag->setMimeData(mimeData);
if (drag->exec(Qt::MoveAction) == Qt::MoveAction)
{
}
else
{
}
}
void TimelineViewDragHandler::mousePressEvent(QMouseEvent *e)
{
if (!m_view->timelineModel()->isEditable())
return;
if (e->button() != Qt::LeftButton)
return;
const QModelIndex index = m_view->indexAt(e->pos());
if (!index.isValid() || index.column() == Section::eActuator)
return;
try
{
Track &track = m_view->timelineModel()->track(index.data().toInt());
Item &item = track.itemAt(m_view->toTime(e->pos().x()));
m_dragItemIds = m_view->m_selectedItemIds;
if (!m_dragItemIds.contains(item.id()))
m_dragItemIds = { item.id() };
for (const int &itemId : m_dragItemIds)
if (!m_view->timelineModel()->item(itemId).isEditable())
return;
m_dragStartIndex = index;
m_dragStartTime = m_view->toTime(e->pos().x());
m_dragStartPos = e->pos();
m_dragPos = m_dragStartPos;
m_view->setCursor(QCursor(Qt::ClosedHandCursor));
}
catch (const QString &str) { DEBUG_LOG(m_view->m_logger) << LOGFUNCTION << " " << str; }
catch (...) {}
}
void TimelineViewDragHandler::mouseReleaseEvent(QMouseEvent *e)
{
if (!m_view->timelineModel()->isEditable())
return;
if (e->button() != Qt::LeftButton)
return;
if (m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid())
return;
const QModelIndex index = m_view->indexAt(e->pos());
if (!index.isValid() || index.column() == Section::eActuator)
return;
if (index.data().toInt() != m_dragStartIndex.data().toInt())
return;
m_dragPos = e->pos();
if (m_dragPos.x() == m_dragStartPos.x())
{
clear();
return;
}
if (!canMoveItemsTo(m_dragPos))
{
clear();
return;
}
emit m_view->currentItemTimeChanged();
qint64 dt = deltaTime();
for (const int &itemId : m_dragItemIds)
{
Item &item = m_view->timelineModel()->item(itemId);
m_view->timelineModel()->setItemStartTime(item, item.startTime() + dt);
}
clear();
}
void TimelineViewDragHandler::keyPressEvent(QKeyEvent *e)
{
if (!m_view->timelineModel()->isEditable())
return;
if (m_view->m_selectedItemIds.isEmpty())
return;
if (e->key() == Qt::Key_Escape)
{
clear();
return;
}
if (e->key() != Qt::Key_Left && e->key() != Qt::Key_Right)
return;
int dt = m_view->timelineModel()->snapStep();
if (e->key() == Qt::Key_Left)
moveItemsLeft(m_view->selectedItemIds(), QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? -dt : -10 * dt);
else
moveItemsRight(m_view->selectedItemIds(), QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? dt : 10 * dt);
}
bool TimelineViewDragHandler::isActive() const
{
return !m_dragItemIds.isEmpty() && (m_dragPos - m_dragStartPos).manhattanLength() >= QApplication::startDragDistance();
}
int TimelineViewDragHandler::deltaX() const
{
if (!m_view->timelineModel()->isSnapEnabled())
return m_dragPos.x() - m_dragStartPos.x();
return m_view->toPixel(m_dragStartTime + deltaTime()) - m_view->toPixel(m_dragStartTime);
}
int TimelineViewDragHandler::deltaY() const
{
const QModelIndex dragIndex = m_view->indexAt(m_dragPos);
if (!dragIndex.isValid())
return m_dragPos.y() - m_dragStartPos.y();
return m_view->visualRect(dragIndex).top() - m_view->visualRect(m_dragStartIndex).top();
}
qint64 TimelineViewDragHandler::deltaTime() const
{
if (!m_view->timelineModel()->isSnapEnabled())
return m_view->toTime(m_dragPos.x()) - m_view->toTime(m_dragStartPos.x());
qint64 dt = ((m_view->toTime(m_dragPos.x()) - m_dragStartTime) / m_view->timelineModel()->snapStep()) * m_view->timelineModel()->snapStep();
int dtAbsMin = 2 * m_view->timelineModel()->snapTolerance();
int eps = m_view->timelineModel()->snapTolerance();
for (const int &dragItemId : m_dragItemIds)
{
const Item &dragItem = m_view->timelineModel()->item(dragItemId);
qint64 start = dragItem.startTime() + dt;
qint64 end = start + dragItem.duration();
for (int itemId : m_view->timelineModel()->itemIds())
{
if (m_dragItemIds.contains(itemId))
continue;
const Item &item = m_view->timelineModel()->item(itemId);
if (start - eps < item.startTime() + item.duration() && item.startTime() + item.duration() < start + eps)
{
qint64 dtCurrent = item.startTime() + item.duration() - dragItem.startTime();
qint64 dtAbsCurrent = std::abs(dtCurrent);
if (dtAbsCurrent < dtAbsMin)
{
dtAbsMin = dtAbsCurrent;
dt = dtCurrent;
}
}
else if (end - eps < item.startTime() && item.startTime() < end + eps)
{
qint64 dtCurrent = item.startTime() - (dragItem.startTime() + dragItem.duration());
qint64 dtAbsCurrent = std::abs(dtCurrent);
if (dtAbsCurrent < dtAbsMin)
{
dtAbsMin = dtAbsCurrent;
dt = dtCurrent;
}
}
}
}
return dt;
}
bool TimelineViewDragHandler::canDragItemsTo(const QPoint &point) const
{
if (!m_view->timelineModel()->isEditable())
return false;
if (m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid())
return false;
const QModelIndex index = m_view->indexAt(point);
if (!index.isValid() || index.column() == Section::eActuator)
return false;
if (index.data().toInt() == m_dragStartIndex.data().toInt())
return canMoveItemsTo(point);
else
{
try
{
qint64 dt = deltaTime();
for (const int &itemId : m_dragItemIds)
{
const Item &item = m_view->timelineModel()->item(itemId);
int trackId = dragItemTrackId(item);
if (trackId == 0)
return false;
const Track &track = m_view->timelineModel()->track(trackId);
qint64 startNew = item.startTime() + dt;
if (!m_view->timelineModel()->canDropItem(item, track.id(), startNew))
return false;
if (track.isItemsBetween(startNew, startNew + item.duration(), m_dragItemIds, true))
return false;
}
}
catch (...)
{
return true;
}
}
return true;
}
bool TimelineViewDragHandler::canMoveItemsTo(const QPoint &point) const
{
if (!m_view->timelineModel()->isEditable())
return false;
if (m_dragItemIds.isEmpty() || !m_dragStartIndex.isValid())
return false;
const QModelIndex index = m_view->indexAt(point);
if (!index.isValid() || index.column() == Section::eActuator || index.data().toInt() != m_dragStartIndex.data().toInt())
return false;
qint64 dt = deltaTime();
for (const int &itemId : m_dragItemIds)
{
const Item &item = m_view->timelineModel()->item(itemId);
qint64 startNew = item.startTime() + dt;
if (!m_view->timelineModel()->canMoveItem(item, startNew))
return false;
const Track &track = m_view->timelineModel()->track(item.parentTrackId());
if (track.isItemsBetween(startNew, startNew + item.duration(), m_dragItemIds, true))
return false;
}
return true;
}
void TimelineViewDragHandler::moveItemsLeft(const QVector<int> &itemIds, int dt)
{
Q_ASSERT(dt < 0);
try
{
for (int itemId : itemIds)
{
const Item &item = m_view->timelineModel()->item(itemId);
const Track &track = m_view->timelineModel()->track(item.parentTrackId());
if (track.isItemsBetween(item.startTime() + dt, item.startTime() + item.duration() + dt, itemIds, true))
{
if (dt < - m_view->timelineModel()->snapStep())
moveItemsLeft(itemIds, dt + m_view->timelineModel()->snapStep());
return;
}
if (!m_view->timelineModel()->canMoveItem(item, item.startTime() + dt))
{
if (dt < - m_view->timelineModel()->snapStep())
moveItemsLeft(itemIds, dt + m_view->timelineModel()->snapStep());
return;
}
}
}
catch (const QString &errMsg)
{
ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed: " << errMsg;
return;
}
catch (...)
{
ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed";
return;
}
try
{
for (int itemId : itemIds)
{
Item &item = m_view->timelineModel()->item(itemId);
m_view->timelineModel()->setItemStartTime(item, item.startTime() + dt);
}
}
catch (const QString &str) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " " << str; }
catch (...) {}
}
void TimelineViewDragHandler::moveItemsRight(const QVector<int> &itemIds, int dt)
{
Q_ASSERT(dt > 0);
try
{
for (int itemId : itemIds)
{
const Item &item = m_view->timelineModel()->item(itemId);
const Track &track = m_view->timelineModel()->track(item.parentTrackId());
if (track.isItemsBetween(item.startTime() + dt, item.startTime() + item.duration() + dt, itemIds, true))
{
if (dt > m_view->timelineModel()->snapStep())
moveItemsRight(itemIds, dt - m_view->timelineModel()->snapStep());
return;
}
if (!m_view->timelineModel()->canMoveItem(item, item.startTime() + dt))
{
if (dt > m_view->timelineModel()->snapStep())
moveItemsRight(itemIds, dt - m_view->timelineModel()->snapStep());
return;
}
}
}
catch (const QString &errMsg)
{
ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed: " << errMsg;
return;
}
catch (...)
{
ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " failed";
return;
}
try
{
for (int itemId : itemIds)
{
Item &item = m_view->timelineModel()->item(itemId);
m_view->timelineModel()->setItemStartTime(item, item.startTime() + dt);
}
}
catch (const QString &errMsg) { ERROR_LOG(m_view->m_logger) << LOGFUNCTION << " " << errMsg; }
catch (...) {}
}
int TimelineViewDragHandler::dragItemTrackId(const Item &item) const
{
int dy = deltaY();
const QModelIndex index = m_view->indexAt(m_dragStartPos + QPoint(0, dy));
if (index.data().toInt() == m_dragStartIndex.data().toInt())
return item.parentTrackId();
try
{
const QModelIndex originalIndex = m_view->timelineModel()->indexOfTrack(item.parentTrackId());
const QModelIndex newIndex = m_view->indexAt(m_view->visualRect(originalIndex).center() + QPoint(0, dy));
return newIndex.data().toInt();
}
catch (...)
{
return 0;
}
}
void TimelineViewDragHandler::clear()
{
m_dragStartIndex = {};
m_dragStartTime = -1;
m_dragStartPos = {};
m_dragPos = {};
m_dragItemIds.clear();
m_view->setCursor(QCursor(Qt::ArrowCursor));
m_view->viewport()->update();
}
}