308 lines
7.5 KiB
C++
Executable File
308 lines
7.5 KiB
C++
Executable File
#include "tln_timeline_select_handler.h"
|
|
#include "tln_abstract_event_item.h"
|
|
#include "tln_participant_track.h"
|
|
#include "tln_timeline_delegate.h"
|
|
#include "tln_timeline_model.h"
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
namespace Timeline
|
|
{
|
|
|
|
TimelineSelectHandler::TimelineSelectHandler(LtsTimeline::TimelineView *view)
|
|
: LtsTimeline::TimelineViewSelectHandler(view)
|
|
{
|
|
m_model = qobject_cast<TimelineModel *>(view->model());
|
|
if (m_model != nullptr)
|
|
return;
|
|
|
|
// okay, maybe it's a proxy model
|
|
auto proxyModel = qobject_cast<QAbstractProxyModel *>(view->model());
|
|
if (proxyModel != nullptr)
|
|
m_model = qobject_cast<TimelineModel *>(proxyModel->sourceModel());
|
|
|
|
if (m_model == nullptr)
|
|
qWarning() << "TimelineSelectHandler:c_tor: model is not a TimelineModel!";
|
|
|
|
Q_ASSERT(m_model != nullptr);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// PUBLIC
|
|
|
|
void TimelineSelectHandler::mouseDoubleClickEvent(QMouseEvent *e)
|
|
{
|
|
if (m_model == nullptr)
|
|
return;
|
|
|
|
if (e->button() != Qt::LeftButton)
|
|
return;
|
|
|
|
m_view->clearSelectedItems();
|
|
const QModelIndex index(m_view->indexAt(e->pos()));
|
|
|
|
if (!index.isValid())
|
|
return;
|
|
|
|
|
|
if (index.column() == LtsTimeline::Section::eActuator)
|
|
{
|
|
int trackId;
|
|
|
|
try
|
|
{
|
|
bool k = true;
|
|
trackId = index.data(Qt::UserRole).toInt(&k); // ffs
|
|
if (!k)
|
|
{
|
|
qWarning() << "cannot find track by user role" << index;
|
|
return;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
qWarning() << "cannot find track by index" << index;
|
|
return;
|
|
}
|
|
|
|
qWarning() << "TRACK ID 1 =" << trackId;
|
|
|
|
qWarning() << "clicked on actuator column with content (string):" << index.data().toString();
|
|
emit m_view->trackActivated(trackId);
|
|
return;
|
|
}
|
|
|
|
|
|
// at this point an ITEM was / might have been selected:
|
|
int itemId;
|
|
try
|
|
{
|
|
bool k = true;
|
|
int trackId = index.data().toInt(&k);
|
|
|
|
if (!k)
|
|
{
|
|
qWarning() << "cannot find track by data" << index;
|
|
return;
|
|
}
|
|
|
|
qWarning() << "TRACK ID 2 =" << trackId;
|
|
const Track &t = m_model->track(trackId);
|
|
itemId = itemIdAt(t, e->pos().x());
|
|
}
|
|
catch (const QString &msg)
|
|
{
|
|
qWarning() << Q_FUNC_INFO << "cannot get track from index internalId" << msg;
|
|
return;
|
|
}
|
|
|
|
if (itemId != -1)
|
|
{
|
|
emit m_view->itemActivated(itemId);
|
|
return;
|
|
}
|
|
|
|
qWarning() << Q_FUNC_INFO << "no item behind click";
|
|
m_view->clearSelectedItems();
|
|
m_rubberBand.setLeft(e->pos().x());
|
|
m_rubberBand.setTop(e->pos().y());
|
|
m_rubberBand.setSize(QSize(0, 0));
|
|
m_isRubberBandActive = true;
|
|
}
|
|
|
|
void TimelineSelectHandler::mousePressEvent(QMouseEvent *e)
|
|
{
|
|
if (m_model == nullptr)
|
|
return;
|
|
|
|
if (e->button() != Qt::LeftButton)
|
|
return;
|
|
|
|
const QModelIndex index(m_view->indexAt(e->pos()));
|
|
if (index.isValid())
|
|
{
|
|
if (index.column() == LtsTimeline::Section::eActuator)
|
|
{
|
|
qWarning() << "clicked on row with actuator" << index.data().toString();
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
int itemId = itemIdAt(m_model->track(index.data().toInt()), e->pos().x());
|
|
if (itemId != -1)
|
|
{
|
|
m_selectedItemId = itemId;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
m_view->clearSelectedItems();
|
|
m_rubberBand.setLeft(e->pos().x());
|
|
m_rubberBand.setTop(e->pos().y());
|
|
m_rubberBand.setSize(QSize(0, 0));
|
|
m_isRubberBandActive = true;
|
|
return;
|
|
}
|
|
}
|
|
catch (...) {}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "TimelineSelectHandler::mousePressEvent: index invalid";
|
|
}
|
|
|
|
m_view->clearSelectedItems();
|
|
|
|
if (!m_view->isCursorWithinRangeX(e->pos().x()))
|
|
return;
|
|
|
|
m_rubberBand.setLeft(e->pos().x());
|
|
m_rubberBand.setTop(e->pos().y());
|
|
m_rubberBand.setSize(QSize(0, 0));
|
|
m_isRubberBandActive = true;
|
|
}
|
|
|
|
void TimelineSelectHandler::mouseReleaseEvent(QMouseEvent *e)
|
|
{
|
|
if (m_model == nullptr)
|
|
return;
|
|
|
|
if (e->button() != Qt::LeftButton)
|
|
return;
|
|
|
|
if (m_isRubberBandActive)
|
|
{
|
|
if (m_rubberBand.width() > 1 && m_rubberBand.height() > 1) // threshold for rubberband
|
|
m_view->selectItems(getItemsWithinRubberBand());
|
|
|
|
m_isRubberBandActive = false;
|
|
m_rubberBand.setSize(QSize(0, 0));
|
|
m_view->setCursor(QCursor(Qt::ArrowCursor));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
const QModelIndex index = m_view->indexAt(e->pos());
|
|
if (!index.isValid() || index.column() == LtsTimeline::Section::eActuator)
|
|
{
|
|
m_selectedItemId = -1;
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
int itemId = itemIdAt(m_model->track(index.data().toInt()), e->pos().x());
|
|
if (itemId == -1)
|
|
{
|
|
m_view->clearSelectedItems();
|
|
}
|
|
else
|
|
{
|
|
if (itemId == m_selectedItemId)
|
|
{
|
|
if (m_view->selectedItemIds().size() > 1 || !m_view->selectedItemIds().contains(m_selectedItemId))
|
|
m_view->selectItem(m_selectedItemId);
|
|
else
|
|
m_view->unselectItem(m_selectedItemId);
|
|
}
|
|
}
|
|
m_selectedItemId = -1;
|
|
return;
|
|
}
|
|
catch (...)
|
|
{
|
|
m_selectedItemId = -1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// PUBLIC
|
|
|
|
int TimelineSelectHandler::itemIdAt(const LtsTimeline::Track &track, int x) const // TODO: noexcept(false)
|
|
{
|
|
if (m_model == nullptr)
|
|
return -1;
|
|
|
|
qint64 time = m_view->toTime(x);
|
|
|
|
try
|
|
{
|
|
const LtsTimeline::Item &item = track.itemAt(time);
|
|
return item.id();
|
|
}
|
|
catch (...) {}
|
|
|
|
const TrackType trackType = m_model->trackType(track);
|
|
|
|
// check marker track
|
|
if (trackType == TrackType::Marker)
|
|
{
|
|
try
|
|
{
|
|
const qint64 offset = TimelineDelegate::s_zeroDurationItemWidth / 2;
|
|
const QVector<int> itemIds = track.itemIdsBetween(m_view->toTime(x - offset), m_view->toTime(x + offset));
|
|
if (itemIds.isEmpty())
|
|
return -1;
|
|
else if (itemIds.size() == 1)
|
|
return itemIds.first();
|
|
else
|
|
{
|
|
auto it = std::min_element(itemIds.begin(), itemIds.end(), [this, time](int leftId, int rightId)
|
|
{
|
|
return std::abs(m_model->item(leftId).startTime() - time) < std::abs(m_model->item(rightId).startTime() - time);
|
|
});
|
|
return *it;
|
|
}
|
|
}
|
|
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "failed:" << errMsg; }
|
|
catch (...) { qCritical() << LOGFUNCTION << "failed"; }
|
|
return -1;
|
|
}
|
|
|
|
// check zero duration track
|
|
if (trackType == TrackType::Party || trackType == TrackType::Participant || trackType == TrackType::BuildingHits)
|
|
{
|
|
try
|
|
{
|
|
for (const ItemPtr &itemPtr : qAsConst(track.items()))
|
|
{
|
|
const int xItem = m_view->toPixel(itemPtr->startTime());
|
|
if (xItem <= x && x <= xItem + TimelineDelegate::s_zeroDurationItemWidth)
|
|
return itemPtr->id();
|
|
}
|
|
}
|
|
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "failed:" << errMsg; }
|
|
catch (...) { qCritical() << LOGFUNCTION << "failed"; }
|
|
return -1;
|
|
}
|
|
|
|
// check duration track
|
|
try
|
|
{
|
|
const int minDuration = m_model->m_zeroDurationItemWidthTime;
|
|
for (const ItemPtr &itemPtr : qAsConst(track.items()))
|
|
{
|
|
if (itemPtr->duration() == -1 && itemPtr->startTime() >= time)
|
|
return itemPtr->id();
|
|
|
|
int duration = itemPtr->duration() < minDuration ? minDuration : itemPtr->duration();
|
|
if (itemPtr->startTime() <= time && time <= itemPtr->startTime() + duration)
|
|
return itemPtr->id();
|
|
}
|
|
}
|
|
catch (const QString &errMsg) { qCritical() << LOGFUNCTION << "failed:" << errMsg; }
|
|
catch (...) { qCritical() << LOGFUNCTION << "failed"; }
|
|
|
|
return -1;
|
|
}
|
|
|
|
} // namespace Timeline
|