#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 #include namespace Timeline { TimelineSelectHandler::TimelineSelectHandler(LtsTimeline::TimelineView *view) : LtsTimeline::TimelineViewSelectHandler(view) { m_model = qobject_cast(view->model()); if (m_model != nullptr) return; // okay, maybe it's a proxy model auto proxyModel = qobject_cast(view->model()); if (proxyModel != nullptr) m_model = qobject_cast(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 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