File indexing completed on 2024-05-05 04:54:13

0001 /*
0002     SPDX-FileCopyrightText: 2017 Jean-Baptiste Mardelle
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 */
0005 
0006 #include <KLocalizedContext>
0007 
0008 #include "../model/builders/meltBuilder.hpp"
0009 #include "assets/keyframes/model/keyframemodel.hpp"
0010 #include "assets/model/assetparametermodel.hpp"
0011 #include "bin/model/markerlistmodel.hpp"
0012 #include "bin/model/markersortmodel.h"
0013 #include "capture/mediacapture.h"
0014 #include "core.h"
0015 #include "doc/docundostack.hpp"
0016 #include "doc/kdenlivedoc.h"
0017 #include "effects/effectsrepository.hpp"
0018 #include "kdenlivesettings.h"
0019 #include "mainwindow.h"
0020 #include "monitor/monitorproxy.h"
0021 #include "profiles/profilemodel.hpp"
0022 #include "qml/timelineitems.h"
0023 #include "qmltypes/thumbnailprovider.h"
0024 #include "timelinecontroller.h"
0025 #include "timelinewidget.h"
0026 #include "utils/clipboardproxy.hpp"
0027 
0028 #include <QAction>
0029 #include <QActionGroup>
0030 #include <QFontDatabase>
0031 #include <QMenu>
0032 #include <QQmlContext>
0033 #include <QQmlEngine>
0034 #include <QQuickItem>
0035 #include <QSortFilterProxyModel>
0036 #include <QUuid>
0037 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0038 #include "kdeclarative_version.h"
0039 #endif
0040 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || KDECLARATIVE_VERSION > QT_VERSION_CHECK(5, 98, 0)
0041 #include <KQuickIconProvider>
0042 #else
0043 #include <KDeclarative/KDeclarative>
0044 #endif
0045 
0046 const int TimelineWidget::comboScale[] = {1, 2, 4, 8, 15, 30, 50, 75, 100, 150, 200, 300, 500, 800, 1000, 1500, 2000, 3000, 6000, 15000, 30000};
0047 
0048 TimelineWidget::TimelineWidget(const QUuid uuid, QWidget *parent)
0049     : QQuickWidget(parent)
0050     , m_uuid(uuid)
0051 {
0052 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) || KDECLARATIVE_VERSION > QT_VERSION_CHECK(5, 98, 0)
0053     engine()->addImageProvider(QStringLiteral("icon"), new KQuickIconProvider);
0054 #else
0055     KDeclarative::KDeclarative kdeclarative;
0056     kdeclarative.setDeclarativeEngine(engine());
0057     kdeclarative.setupEngine(engine());
0058 #endif
0059     engine()->rootContext()->setContextObject(new KLocalizedContext(this));
0060     setClearColor(palette().window().color());
0061     setMouseTracking(true);
0062     registerTimelineItems();
0063     m_sortModel = std::make_unique<QSortFilterProxyModel>(this);
0064     m_proxy = new TimelineController(this);
0065     connect(m_proxy, &TimelineController::zoneMoved, this, &TimelineWidget::zoneMoved);
0066     connect(m_proxy, &TimelineController::ungrabHack, this, &TimelineWidget::slotUngrabHack);
0067     connect(m_proxy, &TimelineController::regainFocus, this, &TimelineWidget::regainFocus, Qt::DirectConnection);
0068     connect(m_proxy, &TimelineController::stopAudioRecord, this, &TimelineWidget::stopAudioRecord, Qt::DirectConnection);
0069     setResizeMode(QQuickWidget::SizeRootObjectToView);
0070     setVisible(false);
0071     setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
0072     setFocusPolicy(Qt::StrongFocus);
0073     m_favEffects = new QMenu(i18n("Insert an effect..."), this);
0074     m_favCompositions = new QMenu(i18n("Insert a composition..."), this);
0075     installEventFilter(this);
0076     m_targetsMenu = new QMenu(this);
0077 }
0078 
0079 TimelineWidget::~TimelineWidget()
0080 {
0081     delete m_proxy;
0082 }
0083 
0084 void TimelineWidget::updateEffectFavorites()
0085 {
0086     const QMap<QString, QString> effects = sortedItems(KdenliveSettings::favorite_effects(), false);
0087     QMapIterator<QString, QString> i(effects);
0088     m_favEffects->clear();
0089     while (i.hasNext()) {
0090         i.next();
0091         QAction *ac = m_favEffects->addAction(i.key());
0092         ac->setData(i.value());
0093     }
0094 }
0095 
0096 void TimelineWidget::updateTransitionFavorites()
0097 {
0098     const QMap<QString, QString> effects = sortedItems(KdenliveSettings::favorite_transitions(), true);
0099     QMapIterator<QString, QString> i(effects);
0100     m_favCompositions->clear();
0101     while (i.hasNext()) {
0102         i.next();
0103         QAction *ac = m_favCompositions->addAction(i.key());
0104         ac->setData(i.value());
0105     }
0106 }
0107 
0108 const QMap<QString, QString> TimelineWidget::sortedItems(const QStringList &items, bool isTransition)
0109 {
0110     QMap<QString, QString> sortedItems;
0111     for (const QString &effect : items) {
0112         sortedItems.insert(m_proxy->getAssetName(effect, isTransition), effect);
0113     }
0114     return sortedItems;
0115 }
0116 
0117 void TimelineWidget::setTimelineMenu(QMenu *clipMenu, QMenu *compositionMenu, QMenu *timelineMenu, QMenu *guideMenu, QMenu *timelineRulerMenu,
0118                                      QAction *editGuideAction, QMenu *headerMenu, QMenu *thumbsMenu, QMenu *subtitleClipMenu)
0119 {
0120     m_timelineClipMenu = new QMenu(this);
0121     QList<QAction *> cActions = clipMenu->actions();
0122     for (auto &a : cActions) {
0123         m_timelineClipMenu->addAction(a);
0124     }
0125     m_timelineCompositionMenu = new QMenu(this);
0126     cActions = compositionMenu->actions();
0127     for (auto &a : cActions) {
0128         m_timelineCompositionMenu->addAction(a);
0129     }
0130     m_timelineMixMenu = new QMenu(this);
0131     QAction *deleteAction = pCore->window()->actionCollection()->action(QLatin1String("delete_timeline_clip"));
0132     m_timelineMixMenu->addAction(deleteAction);
0133 
0134     m_timelineMenu = new QMenu(this);
0135     cActions = timelineMenu->actions();
0136     for (auto &a : cActions) {
0137         m_timelineMenu->addAction(a);
0138     }
0139     m_timelineRulerMenu = new QMenu(this);
0140     cActions = timelineRulerMenu->actions();
0141     for (auto &a : cActions) {
0142         m_timelineRulerMenu->addAction(a);
0143     }
0144     m_guideMenu = guideMenu;
0145     m_headerMenu = headerMenu;
0146     m_thumbsMenu = thumbsMenu;
0147     m_headerMenu->addMenu(m_thumbsMenu);
0148     m_timelineSubtitleClipMenu = subtitleClipMenu;
0149     m_editGuideAcion = editGuideAction;
0150     updateEffectFavorites();
0151     updateTransitionFavorites();
0152     connect(m_favEffects, &QMenu::triggered, this, [&](QAction *ac) { m_proxy->addEffectToClip(ac->data().toString()); });
0153     connect(m_favCompositions, &QMenu::triggered, this, [&](QAction *ac) { m_proxy->addCompositionToClip(ac->data().toString()); });
0154     connect(m_guideMenu, &QMenu::triggered, this, [&](QAction *ac) { m_proxy->setPosition(ac->data().toInt()); });
0155     connect(m_thumbsMenu, &QMenu::triggered, this,
0156             [&](QAction *ac) { m_proxy->setActiveTrackProperty(QStringLiteral("kdenlive:thumbs_format"), ac->data().toString()); });
0157     // Fix qml focus issue
0158     connect(m_headerMenu, &QMenu::aboutToHide, this, &TimelineWidget::slotUngrabHack, Qt::DirectConnection);
0159     connect(m_timelineClipMenu, &QMenu::aboutToHide, this, &TimelineWidget::slotUngrabHack, Qt::DirectConnection);
0160     connect(m_timelineClipMenu, &QMenu::triggered, this, &TimelineWidget::slotResetContextPos);
0161     connect(m_timelineCompositionMenu, &QMenu::aboutToHide, this, &TimelineWidget::slotUngrabHack, Qt::DirectConnection);
0162     connect(m_timelineRulerMenu, &QMenu::aboutToHide, this, &TimelineWidget::slotUngrabHack, Qt::DirectConnection);
0163     connect(m_timelineMenu, &QMenu::aboutToHide, this, &TimelineWidget::slotUngrabHack, Qt::DirectConnection);
0164     connect(m_timelineMenu, &QMenu::triggered, this, &TimelineWidget::slotResetContextPos);
0165     connect(m_timelineSubtitleClipMenu, &QMenu::aboutToHide, this, &TimelineWidget::slotUngrabHack, Qt::DirectConnection);
0166 
0167     m_timelineClipMenu->addMenu(m_favEffects);
0168     m_timelineClipMenu->addMenu(m_favCompositions);
0169     m_timelineMenu->addMenu(m_favCompositions);
0170 }
0171 
0172 void TimelineWidget::unsetModel()
0173 {
0174     rootContext()->setContextProperty("controller", nullptr);
0175     rootContext()->setContextProperty("multitrack", nullptr);
0176     rootContext()->setContextProperty("timeline", nullptr);
0177     rootContext()->setContextProperty("guidesModel", nullptr);
0178     rootContext()->setContextProperty("subtitleModel", nullptr);
0179     m_sortModel.reset(new QSortFilterProxyModel(this));
0180     m_proxy->prepareClose();
0181 }
0182 
0183 const QUuid &TimelineWidget::getUuid() const
0184 {
0185     return m_uuid;
0186 }
0187 
0188 void TimelineWidget::setModel(const std::shared_ptr<TimelineItemModel> &model, MonitorProxy *proxy)
0189 {
0190     loading = true;
0191     m_sortModel->setSourceModel(model.get());
0192     m_sortModel->setSortRole(TimelineItemModel::SortRole);
0193     m_sortModel->sort(0, Qt::DescendingOrder);
0194     m_proxy->setModel(model);
0195     rootContext()->setContextProperty("multitrack", m_sortModel.get());
0196     rootContext()->setContextProperty("controller", model.get());
0197     rootContext()->setContextProperty("timeline", m_proxy);
0198     rootContext()->setContextProperty("guidesModel", model->getFilteredGuideModel().get());
0199     // Create a unique id for this timeline to prevent thumbnails
0200     // leaking from one project to another because of qml's image caching
0201     rootContext()->setContextProperty("documentId", model->uuid());
0202     rootContext()->setContextProperty("audiorec", pCore->getAudioDevice());
0203     rootContext()->setContextProperty("clipboard", new ClipboardProxy(this));
0204     rootContext()->setContextProperty("miniFont", QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
0205     rootContext()->setContextProperty("subtitleModel", model->getSubtitleModel().get());
0206     const QStringList effs = sortedItems(KdenliveSettings::favorite_effects(), false).values();
0207     const QStringList trans = sortedItems(KdenliveSettings::favorite_transitions(), true).values();
0208 
0209     setSource(QUrl(QStringLiteral("qrc:/qml/timeline.qml")));
0210     engine()->addImageProvider(QStringLiteral("thumbnail"), new ThumbnailProvider);
0211     connect(rootObject(), SIGNAL(mousePosChanged(int)), pCore->window(), SLOT(slotUpdateMousePosition(int)));
0212     connect(rootObject(), SIGNAL(zoomIn(bool)), pCore->window(), SLOT(slotZoomIn(bool)));
0213     connect(rootObject(), SIGNAL(zoomOut(bool)), pCore->window(), SLOT(slotZoomOut(bool)));
0214     connect(rootObject(), SIGNAL(processingDrag(bool)), pCore->window(), SIGNAL(enableUndo(bool)));
0215     connect(m_proxy, &TimelineController::seeked, proxy, &MonitorProxy::setPosition);
0216     rootObject()->setProperty("dar", pCore->getCurrentDar());
0217     connect(rootObject(), SIGNAL(showClipMenu(int)), this, SLOT(showClipMenu(int)));
0218     connect(rootObject(), SIGNAL(showMixMenu(int)), this, SLOT(showMixMenu(int)));
0219     connect(rootObject(), SIGNAL(showCompositionMenu()), this, SLOT(showCompositionMenu()));
0220     connect(rootObject(), SIGNAL(showTimelineMenu()), this, SLOT(showTimelineMenu()));
0221     connect(rootObject(), SIGNAL(showRulerMenu()), this, SLOT(showRulerMenu()));
0222     connect(rootObject(), SIGNAL(showHeaderMenu()), this, SLOT(showHeaderMenu()));
0223     connect(rootObject(), SIGNAL(showTargetMenu(int)), this, SLOT(showTargetMenu(int)));
0224     connect(rootObject(), SIGNAL(showSubtitleClipMenu()), this, SLOT(showSubtitleClipMenu()));
0225     m_proxy->setRoot(rootObject());
0226     setVisible(true);
0227     loading = false;
0228     m_proxy->checkDuration();
0229 }
0230 
0231 void TimelineWidget::loadMarkerModel()
0232 {
0233     if (m_proxy) {
0234         rootContext()->setContextProperty("guidesModel", m_proxy->getModel()->getFilteredGuideModel().get());
0235     }
0236 }
0237 
0238 void TimelineWidget::mousePressEvent(QMouseEvent *event)
0239 {
0240     Q_EMIT focusProjectMonitor();
0241 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0242     m_clickPos = event->globalPos();
0243 #else
0244     m_clickPos = event->globalPosition().toPoint();
0245 #endif
0246     QQuickWidget::mousePressEvent(event);
0247 }
0248 
0249 void TimelineWidget::showClipMenu(int cid)
0250 {
0251     // Hide not applicable effects
0252     QList<QAction *> effects = m_favEffects->actions();
0253     int tid = model()->getClipTrackId(cid);
0254     bool isAudioTrack = false;
0255     if (tid > -1) {
0256         isAudioTrack = model()->isAudioTrack(tid);
0257     }
0258     m_favCompositions->setEnabled(!isAudioTrack);
0259     for (auto ac : qAsConst(effects)) {
0260         const QString &id = ac->data().toString();
0261         if (EffectsRepository::get()->isAudioEffect(id) != isAudioTrack) {
0262             ac->setVisible(false);
0263         } else {
0264             ac->setVisible(true);
0265         }
0266     }
0267     m_timelineClipMenu->popup(m_clickPos);
0268 }
0269 
0270 void TimelineWidget::showMixMenu(int /*cid*/)
0271 {
0272     // Show mix menu
0273     m_timelineMixMenu->popup(m_clickPos);
0274 }
0275 
0276 void TimelineWidget::showCompositionMenu()
0277 {
0278     m_timelineCompositionMenu->popup(m_clickPos);
0279 }
0280 
0281 void TimelineWidget::showHeaderMenu()
0282 {
0283     bool isAudio = m_proxy->isActiveTrackAudio();
0284     QList<QAction *> menuActions = m_headerMenu->actions();
0285     QList<QAction *> audioActions;
0286     QStringList allowedActions = {QLatin1String("show_track_record"), QLatin1String("separate_channels"), QLatin1String("normalize_channels")};
0287     for (QAction *ac : qAsConst(menuActions)) {
0288         if (allowedActions.contains(ac->data().toString())) {
0289             audioActions << ac;
0290         }
0291     }
0292     if (!isAudio) {
0293         // Video track
0294         int currentThumbs = m_proxy->getActiveTrackProperty(QStringLiteral("kdenlive:thumbs_format")).toInt();
0295         QList<QAction *> actions = m_thumbsMenu->actions();
0296         for (QAction *ac : qAsConst(actions)) {
0297             if (ac->data().toInt() == currentThumbs) {
0298                 ac->setChecked(true);
0299                 break;
0300             }
0301         }
0302         m_thumbsMenu->menuAction()->setVisible(true);
0303         for (auto ac : qAsConst(audioActions)) {
0304             ac->setVisible(false);
0305         }
0306     } else {
0307         // Audio track
0308         m_thumbsMenu->menuAction()->setVisible(false);
0309         for (auto ac : qAsConst(audioActions)) {
0310             ac->setVisible(true);
0311             if (ac->data().toString() == QLatin1String("show_track_record")) {
0312                 ac->setChecked(m_proxy->getActiveTrackProperty(QStringLiteral("kdenlive:audio_rec")).toInt() == 1);
0313             }
0314         }
0315     }
0316     m_headerMenu->popup(m_clickPos);
0317 }
0318 
0319 void TimelineWidget::showTargetMenu(int tid)
0320 {
0321     int currentTargetStream;
0322     if (tid == -1) {
0323         // Called through shortcut
0324         tid = m_proxy->activeTrack();
0325         if (tid == -1) {
0326             return;
0327         }
0328         if (m_proxy->clipTargets() < 2 || !model()->isAudioTrack(tid)) {
0329             pCore->displayMessage(i18n("No available stream"), MessageType::ErrorMessage);
0330             return;
0331         }
0332         QVariant returnedValue;
0333         QMetaObject::invokeMethod(rootObject(), "getActiveTrackStreamPos", Qt::DirectConnection, Q_RETURN_ARG(QVariant, returnedValue));
0334         m_clickPos = mapToGlobal(QPoint(5, y())) + QPoint(0, returnedValue.toInt());
0335     }
0336     QMap<int, QString> possibleTargets = m_proxy->getCurrentTargets(tid, currentTargetStream);
0337     m_targetsMenu->clear();
0338     if (m_targetsGroup) {
0339         delete m_targetsGroup;
0340     }
0341     m_targetsGroup = new QActionGroup(this);
0342     QMapIterator<int, QString> i(possibleTargets);
0343     while (i.hasNext()) {
0344         i.next();
0345         QAction *ac = m_targetsMenu->addAction(i.value());
0346         ac->setData(i.key());
0347         m_targetsGroup->addAction(ac);
0348         ac->setCheckable(true);
0349         if (i.key() == currentTargetStream) {
0350             ac->setChecked(true);
0351         }
0352     }
0353     connect(m_targetsGroup, &QActionGroup::triggered, this, [this, tid](QAction *action) {
0354         int targetStream = action->data().toInt();
0355         m_proxy->assignAudioTarget(tid, targetStream);
0356     });
0357     if (m_targetsMenu->isEmpty() || possibleTargets.isEmpty()) {
0358         m_headerMenu->popup(m_clickPos);
0359     } else {
0360         m_targetsMenu->popup(m_clickPos);
0361     }
0362 }
0363 
0364 void TimelineWidget::showRulerMenu()
0365 {
0366     m_guideMenu->clear();
0367     const QList<CommentedTime> guides = pCore->currentDoc()->getGuideModel(m_uuid)->getAllMarkers();
0368     m_editGuideAcion->setEnabled(false);
0369     double fps = pCore->getCurrentFps();
0370     int currentPos = rootObject()->property("consumerPosition").toInt();
0371     for (const auto &guide : guides) {
0372         auto *ac = new QAction(guide.comment(), this);
0373         int frame = guide.time().frames(fps);
0374         ac->setData(frame);
0375         if (frame == currentPos) {
0376             m_editGuideAcion->setEnabled(true);
0377         }
0378         m_guideMenu->addAction(ac);
0379     }
0380     m_timelineRulerMenu->popup(m_clickPos);
0381 }
0382 
0383 void TimelineWidget::showTimelineMenu()
0384 {
0385     m_guideMenu->clear();
0386     const QList<CommentedTime> guides = pCore->currentDoc()->getGuideModel(m_uuid)->getAllMarkers();
0387     m_editGuideAcion->setEnabled(false);
0388     double fps = pCore->getCurrentFps();
0389     int currentPos = rootObject()->property("consumerPosition").toInt();
0390     for (const auto &guide : guides) {
0391         auto ac = new QAction(guide.comment(), this);
0392         int frame = guide.time().frames(fps);
0393         ac->setData(frame);
0394         if (frame == currentPos) {
0395             m_editGuideAcion->setEnabled(true);
0396         }
0397         m_guideMenu->addAction(ac);
0398     }
0399     m_timelineMenu->popup(m_clickPos);
0400 }
0401 
0402 void TimelineWidget::showSubtitleClipMenu()
0403 {
0404     m_timelineSubtitleClipMenu->popup(m_clickPos);
0405 }
0406 
0407 void TimelineWidget::slotChangeZoom(int value, bool zoomOnMouse)
0408 {
0409     double pixelScale = QFontMetrics(font()).maxWidth() * 2;
0410     m_proxy->setScaleFactorOnMouse(pixelScale / comboScale[value], zoomOnMouse);
0411 }
0412 
0413 void TimelineWidget::slotCenterView()
0414 {
0415     QMetaObject::invokeMethod(rootObject(), "centerViewOnCursor");
0416 }
0417 
0418 void TimelineWidget::slotFitZoom()
0419 {
0420     QVariant returnedValue;
0421     double prevScale = m_proxy->scaleFactor();
0422     QMetaObject::invokeMethod(rootObject(), "fitZoom", Qt::DirectConnection, Q_RETURN_ARG(QVariant, returnedValue));
0423     double scale = returnedValue.toDouble();
0424     QMetaObject::invokeMethod(rootObject(), "scrollPos", Qt::DirectConnection, Q_RETURN_ARG(QVariant, returnedValue));
0425     int scrollPos = returnedValue.toInt();
0426     if (qFuzzyCompare(prevScale, scale) && scrollPos == 0) {
0427         scale = m_prevScale;
0428         scrollPos = m_scrollPos;
0429     } else {
0430         m_prevScale = prevScale;
0431         m_scrollPos = scrollPos;
0432         scrollPos = 0;
0433     }
0434     m_proxy->setScaleFactorOnMouse(scale, false);
0435     // Update zoom slider
0436     Q_EMIT m_proxy->updateZoom(scale);
0437     QMetaObject::invokeMethod(rootObject(), "goToStart", Q_ARG(QVariant, scrollPos));
0438 }
0439 
0440 Mlt::Tractor *TimelineWidget::tractor()
0441 {
0442     return m_proxy->tractor();
0443 }
0444 
0445 TimelineController *TimelineWidget::controller()
0446 {
0447     return m_proxy;
0448 }
0449 
0450 std::shared_ptr<TimelineItemModel> TimelineWidget::model()
0451 {
0452     return m_proxy->getModel();
0453 }
0454 
0455 void TimelineWidget::zoneUpdated(const QPoint &zone)
0456 {
0457     m_proxy->setZone(zone, false);
0458 }
0459 
0460 void TimelineWidget::zoneUpdatedWithUndo(const QPoint &oldZone, const QPoint &newZone)
0461 {
0462     m_proxy->updateZone(oldZone, newZone);
0463 }
0464 
0465 void TimelineWidget::setTool(ToolType::ProjectTool tool)
0466 {
0467     rootObject()->setProperty("activeTool", int(tool));
0468 }
0469 
0470 ToolType::ProjectTool TimelineWidget::activeTool()
0471 {
0472     return ToolType::ProjectTool(rootObject()->property("activeTool").toInt());
0473 }
0474 
0475 QPair<int, int> TimelineWidget::getAvTracksCount() const
0476 {
0477     return m_proxy->getAvTracksCount();
0478 }
0479 
0480 void TimelineWidget::slotUngrabHack()
0481 {
0482     // Workaround bug: https://bugreports.qt.io/browse/QTBUG-59044
0483     // https://phabricator.kde.org/D5515
0484     QTimer::singleShot(250, this, [this]() {
0485         // Reset menu position, necessary if user closes the menu without selecting any action
0486         rootObject()->setProperty("clickFrame", -1);
0487     });
0488     if (quickWindow()) {
0489         if (quickWindow()->mouseGrabberItem()) {
0490             quickWindow()->mouseGrabberItem()->ungrabMouse();
0491             QPoint mousePos = mapFromGlobal(QCursor::pos());
0492             QMetaObject::invokeMethod(rootObject(), "regainFocus", Qt::DirectConnection, Q_ARG(QVariant, mousePos));
0493         } else {
0494             QMetaObject::invokeMethod(rootObject(), "endDrag", Qt::DirectConnection);
0495         }
0496     }
0497 }
0498 
0499 void TimelineWidget::slotResetContextPos(QAction *)
0500 {
0501     rootObject()->setProperty("clickFrame", -1);
0502     m_clickPos = QPoint();
0503 }
0504 
0505 int TimelineWidget::zoomForScale(double value) const
0506 {
0507     int scale = int(100 / value);
0508     int ix = 13;
0509     while (comboScale[ix] > scale && ix > 0) {
0510         ix--;
0511     }
0512     return ix;
0513 }
0514 
0515 void TimelineWidget::focusTimeline()
0516 {
0517     setFocus();
0518     if (rootObject()) {
0519         rootObject()->setFocus(true);
0520     }
0521 }
0522 
0523 void TimelineWidget::endDrag()
0524 {
0525     if (rootObject()) {
0526         QMetaObject::invokeMethod(rootObject(), "endBinDrag");
0527     }
0528 }
0529 
0530 void TimelineWidget::startAudioRecord(int tid)
0531 {
0532     if (rootObject()) {
0533         QMetaObject::invokeMethod(rootObject(), "startAudioRecord", Qt::DirectConnection, Q_ARG(QVariant, tid));
0534     }
0535 }
0536 
0537 void TimelineWidget::stopAudioRecord()
0538 {
0539     if (rootObject()) {
0540         QMetaObject::invokeMethod(rootObject(), "stopAudioRecord", Qt::DirectConnection);
0541     }
0542 }
0543 
0544 void TimelineWidget::focusInEvent(QFocusEvent *event)
0545 {
0546     QQuickWidget::focusInEvent(event);
0547     QTimer::singleShot(250, rootObject(), SLOT(forceActiveFocus()));
0548 }
0549 
0550 bool TimelineWidget::eventFilter(QObject *object, QEvent *event)
0551 {
0552     switch (event->type()) {
0553     case QEvent::Enter:
0554         if (!hasFocus()) {
0555             Q_EMIT pCore->window()->showTimelineFocus(true, true);
0556         }
0557         break;
0558     case QEvent::Leave:
0559         if (!hasFocus()) {
0560             Q_EMIT pCore->window()->showTimelineFocus(false, true);
0561         }
0562         break;
0563     case QEvent::FocusOut:
0564         Q_EMIT pCore->window()->showTimelineFocus(false, false);
0565         break;
0566     case QEvent::FocusIn:
0567         Q_EMIT pCore->window()->showTimelineFocus(true, false);
0568         break;
0569     default:
0570         break;
0571     }
0572     return QQuickWidget::eventFilter(object, event);
0573 }
0574 
0575 void TimelineWidget::regainFocus()
0576 {
0577     if (underMouse() && rootObject()) {
0578         QPoint mousePos = mapFromGlobal(QCursor::pos());
0579         QMetaObject::invokeMethod(rootObject(), "regainFocus", Qt::DirectConnection, Q_ARG(QVariant, mousePos));
0580     }
0581 }
0582 
0583 bool TimelineWidget::hasSubtitles() const
0584 {
0585     return m_proxy->getModel()->hasSubtitleModel();
0586 }
0587 
0588 void TimelineWidget::connectSubtitleModel(bool firstConnect)
0589 {
0590     qDebug() << "root context get sub model new function";
0591     if (!model()->hasSubtitleModel()) {
0592         // qDebug()<<"null ptr here at root context";
0593         return;
0594     } else {
0595         // qDebug()<<"null ptr NOT here at root context";
0596         rootObject()->setProperty("showSubtitles", KdenliveSettings::showSubtitles());
0597         if (firstConnect) {
0598             rootContext()->setContextProperty("subtitleModel", model()->getSubtitleModel().get());
0599         }
0600     }
0601 }