File indexing completed on 2024-05-12 08:54:15

0001 /*
0002     SPDX-FileCopyrightText: 2007 Jean-Baptiste Mardelle <jb@kdenlive.org>
0003 
0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "monitormanager.h"
0008 #include "core.h"
0009 #include "doc/kdenlivedoc.h"
0010 #include "kdenlivesettings.h"
0011 #include "mainwindow.h"
0012 #include "timeline2/view/timelinewidget.h"
0013 
0014 #include <mlt++/Mlt.h>
0015 
0016 #include "klocalizedstring.h"
0017 #include <KDualAction>
0018 #include <kwidgetsaddons_version.h>
0019 
0020 #include "kdenlive_debug.h"
0021 #include <QObject>
0022 #include <dialogs/timeremap.h>
0023 #include <timeline2/view/timelinecontroller.h>
0024 
0025 const double MonitorManager::speedArray[6] = {1., 1.5, 2., 3., 5.5, 10.};
0026 
0027 MonitorManager::MonitorManager(QObject *parent)
0028     : QObject(parent)
0029     , m_activeMultiTrack(-1)
0030 
0031 {
0032     setupActions();
0033     refreshTimer.setSingleShot(true);
0034     refreshTimer.setInterval(200);
0035     connect(&refreshTimer, &QTimer::timeout, this, &MonitorManager::forceProjectMonitorRefresh);
0036     connect(pCore.get(), &Core::monitorProfileUpdated, this, [&]() {
0037         QAction *prog = pCore->window()->actionCollection()->action(QStringLiteral("mlt_progressive"));
0038         if (prog) {
0039             prog->setEnabled(!pCore->getProjectProfile().progressive());
0040             slotProgressivePlay(prog->isChecked());
0041         }
0042     });
0043 }
0044 
0045 QAction *MonitorManager::getAction(const QString &name)
0046 {
0047     return pCore->window()->action(name.toUtf8().constData());
0048 }
0049 
0050 void MonitorManager::initMonitors(Monitor *clipMonitor, Monitor *projectMonitor)
0051 {
0052     m_clipMonitor = clipMonitor;
0053     m_projectMonitor = projectMonitor;
0054     m_monitorsList.append(clipMonitor);
0055     m_monitorsList.append(projectMonitor);
0056 }
0057 
0058 void MonitorManager::appendMonitor(AbstractMonitor *monitor)
0059 {
0060     if (!m_monitorsList.contains(monitor)) {
0061         m_monitorsList.append(monitor);
0062     }
0063 }
0064 
0065 void MonitorManager::removeMonitor(AbstractMonitor *monitor)
0066 {
0067     m_monitorsList.removeAll(monitor);
0068 }
0069 
0070 AbstractMonitor *MonitorManager::monitor(Kdenlive::MonitorId monitorName)
0071 {
0072     AbstractMonitor *monitor = nullptr;
0073     for (auto &i : m_monitorsList) {
0074         if (i->id() == monitorName) {
0075             monitor = i;
0076         }
0077     }
0078     return monitor;
0079 }
0080 
0081 void MonitorManager::setConsumerProperty(const QString &name, const QString &value)
0082 {
0083     if (m_clipMonitor) {
0084         m_clipMonitor->setConsumerProperty(name, value);
0085     }
0086     if (m_projectMonitor) {
0087         m_projectMonitor->setConsumerProperty(name, value);
0088     }
0089 }
0090 
0091 void MonitorManager::lockMonitor(Kdenlive::MonitorId name, bool lock)
0092 {
0093     Q_UNUSED(name)
0094     if (lock) {
0095         m_refreshMutex.lock();
0096     } else {
0097         m_refreshMutex.unlock();
0098     }
0099 }
0100 
0101 void MonitorManager::focusProjectMonitor()
0102 {
0103     if (!m_projectMonitor->isActive()) {
0104         activateMonitor(Kdenlive::ProjectMonitor);
0105     } else {
0106         // Force raise
0107         m_projectMonitor->parentWidget()->raise();
0108     }
0109 }
0110 
0111 void MonitorManager::refreshProjectRange(QPair<int, int> range, bool forceRefresh)
0112 {
0113     if (m_projectMonitor->position() >= range.first && m_projectMonitor->position() <= range.second) {
0114         if (forceRefresh) {
0115             m_projectMonitor->refreshMonitor(false);
0116         } else {
0117             m_projectMonitor->refreshMonitorIfActive();
0118         }
0119     }
0120 }
0121 
0122 void MonitorManager::refreshProjectMonitor(bool directUpdate)
0123 {
0124     m_projectMonitor->refreshMonitor(directUpdate);
0125 }
0126 
0127 void MonitorManager::refreshClipMonitor(bool directUpdate)
0128 {
0129     m_clipMonitor->refreshMonitor(directUpdate);
0130 }
0131 
0132 void MonitorManager::forceProjectMonitorRefresh()
0133 {
0134     m_projectMonitor->forceMonitorRefresh();
0135 }
0136 
0137 bool MonitorManager::projectMonitorVisible() const
0138 {
0139     return (m_projectMonitor->monitorIsFullScreen() || (m_projectMonitor->isVisible() && !m_projectMonitor->visibleRegion().isEmpty()));
0140 }
0141 
0142 bool MonitorManager::clipMonitorVisible() const
0143 {
0144     return (m_clipMonitor->monitorIsFullScreen() || (m_clipMonitor->isVisible() && !m_clipMonitor->visibleRegion().isEmpty()));
0145 }
0146 
0147 void MonitorManager::refreshMonitors()
0148 {
0149     if (m_activeMonitor) {
0150         if (m_activeMonitor == m_clipMonitor) {
0151             activateMonitor(Kdenlive::ProjectMonitor);
0152             refreshProjectMonitor(true);
0153             activateMonitor(Kdenlive::ClipMonitor);
0154             refreshClipMonitor(true);
0155         } else {
0156             bool playing = m_projectMonitor->isPlaying();
0157             if (playing) {
0158                 m_projectMonitor->switchPlay(false);
0159             }
0160             activateMonitor(Kdenlive::ClipMonitor);
0161             refreshClipMonitor(true);
0162             activateMonitor(Kdenlive::ProjectMonitor);
0163             refreshProjectMonitor(true);
0164             if (playing) {
0165                 m_projectMonitor->switchPlay(true);
0166             }
0167         }
0168     }
0169 }
0170 
0171 bool MonitorManager::activateMonitor(Kdenlive::MonitorId name, bool raiseMonitor)
0172 {
0173     if ((m_activeMonitor != nullptr) && m_activeMonitor->id() == name) {
0174         return true;
0175     }
0176     if (m_clipMonitor == nullptr || m_projectMonitor == nullptr) {
0177         return false;
0178     }
0179     QMutexLocker locker(&m_switchMutex);
0180     bool stopCurrent = m_activeMonitor != nullptr;
0181     for (int i = 0; i < m_monitorsList.count(); ++i) {
0182         if (m_monitorsList.at(i)->id() == name) {
0183             m_activeMonitor = m_monitorsList.at(i);
0184         } else if (stopCurrent) {
0185             m_monitorsList.at(i)->stop();
0186         }
0187     }
0188     if (m_activeMonitor) {
0189         if (name == Kdenlive::ClipMonitor) {
0190             if (!m_clipMonitor->monitorIsFullScreen()) {
0191                 if (raiseMonitor) {
0192                     m_clipMonitor->parentWidget()->raise();
0193                 }
0194             } else {
0195                 m_clipMonitor->fixFocus();
0196             }
0197             // Set guides list to show guides
0198             m_clipMonitor->updateGuidesList();
0199             if (!m_clipMonitor->isVisible()) {
0200                 pCore->displayMessage(i18n("Do you want to <a href=\"#clipmonitor\">show the clip monitor</a> to view timeline?"),
0201                                       MessageType::InformationMessage);
0202                 m_activeMonitor = m_projectMonitor;
0203                 return false;
0204             }
0205             Q_EMIT updateOverlayInfos(name, KdenliveSettings::displayClipMonitorInfo());
0206             m_projectMonitor->displayAudioMonitor(false);
0207             m_clipMonitor->displayAudioMonitor(true);
0208         } else if (name == Kdenlive::ProjectMonitor) {
0209             // Set guides list to show guides
0210             m_projectMonitor->updateGuidesList();
0211             if (!m_projectMonitor->monitorIsFullScreen()) {
0212                 if (raiseMonitor) {
0213                     m_projectMonitor->parentWidget()->raise();
0214                 }
0215             } else {
0216                 m_projectMonitor->fixFocus();
0217             }
0218             if (!m_projectMonitor->isVisible()) {
0219                 pCore->displayMessage(i18n("Do you want to <a href=\"#projectmonitor\">show the project monitor</a> to view timeline?"),
0220                                       MessageType::InformationMessage);
0221                 m_activeMonitor = m_clipMonitor;
0222                 return false;
0223             }
0224             Q_EMIT updateOverlayInfos(name, KdenliveSettings::displayProjectMonitorInfo());
0225             m_clipMonitor->displayAudioMonitor(false);
0226             m_projectMonitor->displayAudioMonitor(true);
0227         }
0228     }
0229     Q_EMIT checkColorScopes();
0230     return (m_activeMonitor != nullptr);
0231 }
0232 
0233 void MonitorManager::resetDisplay()
0234 {
0235     m_projectMonitor->clearDisplay();
0236     m_clipMonitor->clearDisplay();
0237 }
0238 
0239 bool MonitorManager::isActive(Kdenlive::MonitorId id) const
0240 {
0241     return m_activeMonitor ? m_activeMonitor->id() == id : false;
0242 }
0243 
0244 void MonitorManager::slotSwitchMonitors(bool activateClip)
0245 {
0246     if (activateClip) {
0247         activateMonitor(Kdenlive::ClipMonitor);
0248     } else {
0249         activateMonitor(Kdenlive::ProjectMonitor);
0250     }
0251 }
0252 
0253 void MonitorManager::stopActiveMonitor()
0254 {
0255     if (m_activeMonitor) {
0256         m_activeMonitor->stop();
0257     }
0258 }
0259 
0260 void MonitorManager::pauseActiveMonitor()
0261 {
0262     if (m_activeMonitor == m_clipMonitor) {
0263         m_clipMonitor->pause();
0264     } else if (m_activeMonitor == m_projectMonitor) {
0265         m_projectMonitor->pause();
0266     }
0267 }
0268 
0269 void MonitorManager::slotPlay()
0270 {
0271     if (m_activeMonitor) {
0272         m_activeMonitor->slotPlay();
0273     }
0274 }
0275 
0276 void MonitorManager::slotPause()
0277 {
0278     pauseActiveMonitor();
0279 }
0280 
0281 void MonitorManager::slotPlayZone()
0282 {
0283     if (m_activeMonitor == m_clipMonitor) {
0284         m_clipMonitor->slotPlayZone();
0285     } else if (m_activeMonitor == m_projectMonitor) {
0286         m_projectMonitor->slotPlayZone();
0287     }
0288 }
0289 
0290 void MonitorManager::slotLoopZone()
0291 {
0292     if (m_activeMonitor == m_clipMonitor) {
0293         m_clipMonitor->slotLoopZone();
0294     } else {
0295         m_projectMonitor->slotLoopZone();
0296     }
0297 }
0298 
0299 void MonitorManager::slotRewind(double speed)
0300 {
0301     if (m_activeMonitor) {
0302         m_activeMonitor->slotRewind(speed);
0303     }
0304 }
0305 
0306 void MonitorManager::slotForward(double speed)
0307 {
0308     if (m_activeMonitor) {
0309         m_activeMonitor->slotForward(speed, true);
0310     }
0311 }
0312 
0313 void MonitorManager::slotRewindOneFrame()
0314 {
0315     if (pCore->activeTool() == ToolType::SlipTool) {
0316         m_projectMonitor->slotTrimmingPos(-1);
0317         pCore->window()->getCurrentTimeline()->model()->requestSlipSelection(-1, true);
0318     } else if (isTrimming()) {
0319         return;
0320     } else {
0321         if (m_activeMonitor == m_clipMonitor) {
0322             m_clipMonitor->slotRewindOneFrame();
0323         } else if (m_activeMonitor == m_projectMonitor) {
0324             m_projectMonitor->slotRewindOneFrame();
0325         }
0326     }
0327 }
0328 
0329 void MonitorManager::slotForwardOneFrame()
0330 {
0331     if (pCore->activeTool() == ToolType::SlipTool) {
0332         m_projectMonitor->slotTrimmingPos(1);
0333         pCore->window()->getCurrentTimeline()->model()->requestSlipSelection(1, true);
0334     } else if (isTrimming()) {
0335         return;
0336     } else {
0337         if (m_activeMonitor == m_clipMonitor) {
0338             m_clipMonitor->slotForwardOneFrame();
0339         } else if (m_activeMonitor == m_projectMonitor) {
0340             m_projectMonitor->slotForwardOneFrame();
0341         }
0342     }
0343 }
0344 
0345 void MonitorManager::slotRewindOneSecond()
0346 {
0347     if (pCore->activeTool() == ToolType::SlipTool) {
0348         m_projectMonitor->slotTrimmingPos(-qRound(pCore->getCurrentFps()));
0349         pCore->window()->getCurrentTimeline()->model()->requestSlipSelection(-qRound(pCore->getCurrentFps()), true);
0350     } else if (isTrimming()) {
0351         return;
0352     } else {
0353         if (m_activeMonitor == m_clipMonitor) {
0354             m_clipMonitor->slotRewindOneFrame(qRound(pCore->getCurrentFps()));
0355         } else if (m_activeMonitor == m_projectMonitor) {
0356             m_projectMonitor->slotRewindOneFrame(qRound(pCore->getCurrentFps()));
0357         }
0358     }
0359 }
0360 
0361 void MonitorManager::slotForwardOneSecond()
0362 {
0363     if (pCore->activeTool() == ToolType::SlipTool) {
0364         m_projectMonitor->slotTrimmingPos(qRound(pCore->getCurrentFps()));
0365         pCore->window()->getCurrentTimeline()->model()->requestSlipSelection(qRound(pCore->getCurrentFps()), true);
0366     } else if (isTrimming()) {
0367         return;
0368     } else {
0369         if (m_activeMonitor == m_clipMonitor) {
0370             m_clipMonitor->slotForwardOneFrame(qRound(pCore->getCurrentFps()));
0371         } else if (m_activeMonitor == m_projectMonitor) {
0372             m_projectMonitor->slotForwardOneFrame(qRound(pCore->getCurrentFps()));
0373         }
0374     }
0375 }
0376 
0377 void MonitorManager::slotStartMultiTrackMode()
0378 {
0379     getAction(QStringLiteral("monitor_multitrack"))->setEnabled(false);
0380     m_activeMultiTrack = pCore->window()->getCurrentTimeline()->controller()->activeTrack();
0381     pCore->window()->getCurrentTimeline()->controller()->setMulticamIn(m_projectMonitor->position());
0382 }
0383 
0384 void MonitorManager::slotStopMultiTrackMode()
0385 {
0386     if (m_activeMultiTrack == -1) {
0387         return;
0388     }
0389     getAction(QStringLiteral("monitor_multitrack"))->setEnabled(true);
0390     pCore->window()->getCurrentTimeline()->controller()->setMulticamIn(-1);
0391     m_activeMultiTrack = -1;
0392 }
0393 
0394 void MonitorManager::slotPerformMultiTrackMode()
0395 {
0396     if (m_activeMultiTrack == -1) {
0397         return;
0398     }
0399     pCore->window()->getCurrentTimeline()->controller()->processMultitrackOperation(m_activeMultiTrack,
0400                                                                                     pCore->window()->getCurrentTimeline()->controller()->multicamIn);
0401     m_activeMultiTrack = pCore->window()->getCurrentTimeline()->controller()->activeTrack();
0402     pCore->window()->getCurrentTimeline()->controller()->setMulticamIn(m_projectMonitor->position());
0403 }
0404 
0405 void MonitorManager::slotStart()
0406 {
0407     if (m_activeMonitor == m_clipMonitor) {
0408         m_clipMonitor->slotStart();
0409     } else if (m_activeMonitor == m_projectMonitor) {
0410         m_projectMonitor->slotStart();
0411     }
0412 }
0413 
0414 void MonitorManager::slotEnd()
0415 {
0416     if (m_activeMonitor == m_clipMonitor) {
0417         m_clipMonitor->slotEnd();
0418     } else if (m_activeMonitor == m_projectMonitor) {
0419         m_projectMonitor->slotEnd();
0420     }
0421 }
0422 
0423 void MonitorManager::resetProfiles()
0424 {
0425     if (m_clipMonitor) {
0426         m_clipMonitor->resetProfile();
0427     }
0428     if (m_projectMonitor) {
0429         m_projectMonitor->resetProfile();
0430     }
0431 }
0432 
0433 void MonitorManager::resetConsumers(bool fullReset)
0434 {
0435     bool clipMonitorActive = m_clipMonitor->isActive();
0436     m_clipMonitor->resetConsumer(fullReset);
0437     m_projectMonitor->resetConsumer(fullReset);
0438     if (clipMonitorActive) {
0439         refreshClipMonitor();
0440     } else {
0441         refreshProjectMonitor();
0442     }
0443 }
0444 
0445 void MonitorManager::slotToggleEffectScene(bool enable)
0446 {
0447     if (m_activeMonitor) {
0448         static_cast<Monitor *>(m_activeMonitor)->enableEffectScene(enable);
0449     }
0450 }
0451 
0452 void MonitorManager::slotUpdateAudioMonitoring()
0453 {
0454     if (m_clipMonitor) {
0455         m_clipMonitor->updateAudioForAnalysis();
0456     }
0457     if (m_projectMonitor) {
0458         m_projectMonitor->updateAudioForAnalysis();
0459     }
0460 }
0461 
0462 void MonitorManager::clearScopeSource()
0463 {
0464     Q_EMIT clearScopes();
0465 }
0466 
0467 void MonitorManager::updateScopeSource()
0468 {
0469     Q_EMIT checkColorScopes();
0470 }
0471 
0472 AbstractMonitor *MonitorManager::activeMonitor()
0473 {
0474     if (m_activeMonitor) {
0475         return m_activeMonitor;
0476     }
0477     return nullptr;
0478 }
0479 
0480 void MonitorManager::slotSwitchFullscreen()
0481 {
0482     if (m_activeMonitor) {
0483         m_activeMonitor->slotSwitchFullScreen();
0484     }
0485 }
0486 
0487 void MonitorManager::setupActions()
0488 {
0489     KDualAction *playAction = new KDualAction(i18n("Play"), i18n("Pause"), this);
0490     playAction->setInactiveIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
0491     playAction->setActiveIcon(QIcon::fromTheme(QStringLiteral("media-playback-pause")));
0492     connect(playAction, &KDualAction::activeChangedByUser, this, &MonitorManager::slotPlay);
0493     pCore->window()->addAction(QStringLiteral("monitor_play"), playAction, Qt::Key_Space, QStringLiteral("navandplayback"));
0494 
0495     QAction *monitorPause = new QAction(QIcon::fromTheme(QStringLiteral("media-playback-stop")), i18n("Pause"), this);
0496     connect(monitorPause, &QAction::triggered, this, &MonitorManager::slotPause);
0497     pCore->window()->addAction(QStringLiteral("monitor_pause"), monitorPause, Qt::Key_K, QStringLiteral("navandplayback"));
0498 
0499     QAction *fullMonitor = new QAction(i18n("Switch Monitor Fullscreen"), this);
0500     fullMonitor->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen")));
0501     connect(fullMonitor, &QAction::triggered, this, &MonitorManager::slotSwitchFullscreen);
0502     pCore->window()->addAction(QStringLiteral("monitor_fullscreen"), fullMonitor, Qt::Key_F11, QStringLiteral("monitor"));
0503 
0504     QAction *monitorZoomIn = new QAction(i18n("Zoom In Monitor"), this);
0505     monitorZoomIn->setIcon(QIcon::fromTheme(QStringLiteral("zoom-in")));
0506     connect(monitorZoomIn, &QAction::triggered, this, &MonitorManager::slotZoomIn);
0507     pCore->window()->addAction(QStringLiteral("monitor_zoomin"), monitorZoomIn, {}, QStringLiteral("monitor"));
0508 
0509     QAction *monitorZoomOut = new QAction(i18n("Zoom Out Monitor"), this);
0510     monitorZoomOut->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out")));
0511     connect(monitorZoomOut, &QAction::triggered, this, &MonitorManager::slotZoomOut);
0512     pCore->window()->addAction(QStringLiteral("monitor_zoomout"), monitorZoomOut, {}, QStringLiteral("monitor"));
0513 
0514     QAction *monitorSeekBackward = new QAction(QIcon::fromTheme(QStringLiteral("media-seek-backward")), i18n("Rewind"), this);
0515     connect(monitorSeekBackward, &QAction::triggered, this, [&](bool) { MonitorManager::slotRewind(); });
0516     pCore->window()->addAction(QStringLiteral("monitor_seek_backward"), monitorSeekBackward, Qt::Key_J, QStringLiteral("navandplayback"));
0517 
0518     QAction *monitorSeekBackwardOneFrame = new QAction(QIcon::fromTheme(QStringLiteral("media-skip-backward")), i18n("Rewind 1 Frame"), this);
0519     connect(monitorSeekBackwardOneFrame, &QAction::triggered, this, &MonitorManager::slotRewindOneFrame);
0520     pCore->window()->addAction(QStringLiteral("monitor_seek_backward-one-frame"), monitorSeekBackwardOneFrame, Qt::Key_Left, QStringLiteral("navandplayback"));
0521 
0522     QAction *monitorSeekBackwardOneSecond = new QAction(QIcon::fromTheme(QStringLiteral("media-skip-backward")), i18n("Rewind 1 Second"), this);
0523     connect(monitorSeekBackwardOneSecond, &QAction::triggered, this, &MonitorManager::slotRewindOneSecond);
0524     pCore->window()->addAction(QStringLiteral("monitor_seek_backward-one-second"), monitorSeekBackwardOneSecond, Qt::SHIFT | Qt::Key_Left,
0525                                QStringLiteral("navandplayback"));
0526 
0527     QAction *monitorSeekForward = new QAction(QIcon::fromTheme(QStringLiteral("media-seek-forward")), i18n("Forward"), this);
0528     connect(monitorSeekForward, &QAction::triggered, this, [&](bool) { MonitorManager::slotForward(); });
0529     pCore->window()->addAction(QStringLiteral("monitor_seek_forward"), monitorSeekForward, Qt::Key_L, QStringLiteral("navandplayback"));
0530 
0531     QAction *projectStart = new QAction(QIcon::fromTheme(QStringLiteral("go-first")), i18n("Go to Project Start"), this);
0532     connect(projectStart, &QAction::triggered, this, &MonitorManager::slotStart);
0533     pCore->window()->addAction(QStringLiteral("seek_start"), projectStart, Qt::CTRL | Qt::Key_Home, QStringLiteral("navandplayback"));
0534 
0535     QAction *projectEnd = new QAction(QIcon::fromTheme(QStringLiteral("go-last")), i18n("Go to Project End"), this);
0536     connect(projectEnd, &QAction::triggered, this, &MonitorManager::slotEnd);
0537     pCore->window()->addAction(QStringLiteral("seek_end"), projectEnd, Qt::CTRL | Qt::Key_End, QStringLiteral("navandplayback"));
0538 
0539     QAction *monitorSeekForwardOneFrame = new QAction(QIcon::fromTheme(QStringLiteral("media-skip-forward")), i18n("Forward 1 Frame"), this);
0540     connect(monitorSeekForwardOneFrame, &QAction::triggered, this, &MonitorManager::slotForwardOneFrame);
0541     pCore->window()->addAction(QStringLiteral("monitor_seek_forward-one-frame"), monitorSeekForwardOneFrame, Qt::Key_Right, QStringLiteral("navandplayback"));
0542 
0543     QAction *monitorSeekForwardOneSecond = new QAction(QIcon::fromTheme(QStringLiteral("media-skip-forward")), i18n("Forward 1 Second"), this);
0544     connect(monitorSeekForwardOneSecond, &QAction::triggered, this, &MonitorManager::slotForwardOneSecond);
0545     pCore->window()->addAction(QStringLiteral("monitor_seek_forward-one-second"), monitorSeekForwardOneSecond, Qt::SHIFT | Qt::Key_Right,
0546                                QStringLiteral("navandplayback"));
0547 
0548     m_multiTrack = new QAction(QIcon::fromTheme(QStringLiteral("view-split-left-right")), i18n("Multitrack View"), this);
0549     m_multiTrack->setCheckable(true);
0550     connect(m_multiTrack, &QAction::triggered, this, [&](bool checked) {
0551         if (m_projectMonitor) {
0552             Q_EMIT m_projectMonitor->multitrackView(checked, true);
0553         }
0554     });
0555     pCore->window()->addAction(QStringLiteral("monitor_multitrack"), m_multiTrack, Qt::Key_F12, QStringLiteral("monitor"));
0556 
0557     QAction *performMultiTrackOperation = new QAction(QIcon::fromTheme(QStringLiteral("media-playback-pause")), i18n("Perform Multitrack Operation"), this);
0558     connect(performMultiTrackOperation, &QAction::triggered, this, &MonitorManager::slotPerformMultiTrackMode);
0559     pCore->window()->addAction(QStringLiteral("perform_multitrack_mode"), performMultiTrackOperation);
0560 
0561     QAction *enableEditmode = new QAction(QIcon::fromTheme(QStringLiteral("transform-crop")), i18n("Show/Hide edit mode"), this);
0562     enableEditmode->setWhatsThis(xi18nc("@info:whatsthis", "Toggles edit mode (and the display of the object handles)."));
0563     enableEditmode->setCheckable(true);
0564     enableEditmode->setChecked(KdenliveSettings::showOnMonitorScene());
0565     connect(enableEditmode, &QAction::triggered, this, &MonitorManager::slotToggleEffectScene);
0566     pCore->window()->addAction(QStringLiteral("monitor_editmode"), enableEditmode, {}, QStringLiteral("monitor"));
0567 
0568     KSelectAction *interlace = new KSelectAction(i18n("Deinterlacer"), this);
0569     interlace->addAction(i18n("One Field (fast)"));
0570     interlace->addAction(i18n("Linear Blend (fast)"));
0571     interlace->addAction(i18n("YADIF - temporal only (good)"));
0572     interlace->addAction(i18n("YADIF - temporal + spacial (best)"));
0573     if (KdenliveSettings::mltdeinterlacer() == QLatin1String("linearblend")) {
0574         interlace->setCurrentItem(1);
0575     } else if (KdenliveSettings::mltdeinterlacer() == QLatin1String("yadif-nospatial")) {
0576         interlace->setCurrentItem(2);
0577     } else if (KdenliveSettings::mltdeinterlacer() == QLatin1String("yadif")) {
0578         interlace->setCurrentItem(3);
0579     } else {
0580         interlace->setCurrentItem(0);
0581     }
0582     connect(interlace, &KSelectAction::indexTriggered, this, &MonitorManager::slotSetDeinterlacer);
0583     pCore->window()->addAction(QStringLiteral("mlt_interlace"), interlace);
0584     pCore->window()->actionCollection()->setShortcutsConfigurable(interlace, false);
0585 
0586     KSelectAction *interpol = new KSelectAction(i18n("Interpolation"), this);
0587     interpol->addAction(i18n("Nearest Neighbor (fast)"));
0588     interpol->addAction(i18n("Bilinear (good)"));
0589     interpol->addAction(i18n("Bicubic (better)"));
0590     interpol->addAction(i18n("Hyper/Lanczos (best)"));
0591     if (KdenliveSettings::mltinterpolation() == QLatin1String("bilinear")) {
0592         interpol->setCurrentItem(1);
0593     } else if (KdenliveSettings::mltinterpolation() == QLatin1String("bicubic")) {
0594         interpol->setCurrentItem(2);
0595     } else if (KdenliveSettings::mltinterpolation() == QLatin1String("hyper")) {
0596         interpol->setCurrentItem(3);
0597     } else {
0598         interpol->setCurrentItem(0);
0599     }
0600     connect(interpol, &KSelectAction::indexTriggered, this, &MonitorManager::slotSetInterpolation);
0601     pCore->window()->addAction(QStringLiteral("mlt_interpolation"), interpol);
0602     pCore->window()->actionCollection()->setShortcutsConfigurable(interpol, false);
0603 
0604     QAction *progressive = new QAction(i18n("Progressive playback"), this);
0605     connect(progressive, &QAction::triggered, this, &MonitorManager::slotProgressivePlay);
0606     pCore->window()->addAction(QStringLiteral("mlt_progressive"), progressive);
0607     progressive->setCheckable(true);
0608     progressive->setChecked(KdenliveSettings::monitor_progressive());
0609 
0610     QAction *audioScrub = new QAction(i18n("Audio Scrubbing"), this);
0611     connect(audioScrub, &QAction::triggered, this, [&](bool enable) { KdenliveSettings::setAudio_scrub(enable); });
0612     pCore->window()->addAction(QStringLiteral("mlt_scrub"), audioScrub);
0613     audioScrub->setCheckable(true);
0614     audioScrub->setChecked(KdenliveSettings::audio_scrub());
0615 
0616     m_muteAction = new KDualAction(i18n("Mute Monitor"), i18n("Unmute Monitor"), this);
0617     m_muteAction->setActiveIcon(QIcon::fromTheme(QStringLiteral("audio-volume-medium")));
0618     m_muteAction->setInactiveIcon(QIcon::fromTheme(QStringLiteral("audio-volume-muted")));
0619     connect(m_muteAction, &KDualAction::activeChangedByUser, this, &MonitorManager::slotMuteCurrentMonitor);
0620     pCore->window()->addAction(QStringLiteral("mlt_mute"), m_muteAction, {}, QStringLiteral("monitor"));
0621 
0622     QAction *zoneStart = new QAction(QIcon::fromTheme(QStringLiteral("media-seek-backward")), i18n("Go to Zone Start"), this);
0623     connect(zoneStart, &QAction::triggered, this, &MonitorManager::slotZoneStart);
0624     pCore->window()->addAction(QStringLiteral("seek_zone_start"), zoneStart, Qt::SHIFT | Qt::Key_I, QStringLiteral("navandplayback"));
0625 
0626     QAction *zoneEnd = new QAction(QIcon::fromTheme(QStringLiteral("media-seek-forward")), i18n("Go to Zone End"), this);
0627     connect(zoneEnd, &QAction::triggered, this, &MonitorManager::slotZoneEnd);
0628     pCore->window()->addAction(QStringLiteral("seek_zone_end"), zoneEnd, Qt::SHIFT | Qt::Key_O, QStringLiteral("navandplayback"));
0629 
0630     QAction *markIn = new QAction(QIcon::fromTheme(QStringLiteral("zone-in")), i18n("Set Zone In"), this);
0631     connect(markIn, &QAction::triggered, this, &MonitorManager::slotSetInPoint);
0632     pCore->window()->addAction(QStringLiteral("mark_in"), markIn, Qt::Key_I);
0633 
0634     QAction *markOut = new QAction(QIcon::fromTheme(QStringLiteral("zone-out")), i18n("Set Zone Out"), this);
0635     connect(markOut, &QAction::triggered, this, &MonitorManager::slotSetOutPoint);
0636     pCore->window()->addAction(QStringLiteral("mark_out"), markOut, Qt::Key_O);
0637 }
0638 
0639 void MonitorManager::refreshIcons()
0640 {
0641     QList<QAction *> allMenus = this->findChildren<QAction *>();
0642     for (int i = 0; i < allMenus.count(); i++) {
0643         QAction *m = allMenus.at(i);
0644         QIcon ic = m->icon();
0645         if (ic.isNull() || ic.name().isEmpty()) {
0646             continue;
0647         }
0648         QIcon newIcon = QIcon::fromTheme(ic.name());
0649         m->setIcon(newIcon);
0650     }
0651 }
0652 
0653 void MonitorManager::slotSetDeinterlacer(int ix)
0654 {
0655     QString value;
0656     switch (ix) {
0657 
0658     case 1:
0659         value = QStringLiteral("linearblend");
0660         break;
0661     case 2:
0662         value = QStringLiteral("yadif-nospatial");
0663         break;
0664     case 3:
0665         value = QStringLiteral("yadif");
0666         break;
0667     default:
0668         value = QStringLiteral("onefield");
0669     }
0670     KdenliveSettings::setMltdeinterlacer(value);
0671     setConsumerProperty(QStringLiteral("deinterlacer"), value);
0672 }
0673 
0674 void MonitorManager::slotSetInterpolation(int ix)
0675 {
0676     QString value;
0677     switch (ix) {
0678     case 1:
0679         value = QStringLiteral("bilinear");
0680         break;
0681     case 2:
0682         value = QStringLiteral("bicubic");
0683         break;
0684     case 3:
0685         value = QStringLiteral("hyper");
0686         break;
0687     default:
0688         value = QStringLiteral("nearest");
0689     }
0690     KdenliveSettings::setMltinterpolation(value);
0691     setConsumerProperty(QStringLiteral("rescale"), value);
0692 }
0693 
0694 void MonitorManager::slotMuteCurrentMonitor(bool active)
0695 {
0696     m_activeMonitor->mute(active);
0697 }
0698 
0699 void MonitorManager::slotProgressivePlay(bool active)
0700 {
0701     if (pCore->getProjectProfile().progressive()) {
0702         // nothing to do
0703         return;
0704     }
0705     KdenliveSettings::setMonitor_progressive(active);
0706     if (m_clipMonitor) {
0707         m_clipMonitor->resetConsumer(true);
0708         m_clipMonitor->refreshMonitor(true);
0709     }
0710     if (m_projectMonitor) {
0711         m_projectMonitor->resetConsumer(true);
0712         m_projectMonitor->refreshMonitor(true);
0713     }
0714 }
0715 
0716 Monitor *MonitorManager::clipMonitor()
0717 {
0718     return m_clipMonitor;
0719 }
0720 
0721 Monitor *MonitorManager::projectMonitor()
0722 {
0723     return m_projectMonitor;
0724 }
0725 
0726 void MonitorManager::slotZoneStart()
0727 {
0728     if (m_activeMonitor == m_clipMonitor) {
0729         m_clipMonitor->slotZoneStart();
0730     } else if (m_activeMonitor == m_projectMonitor) {
0731         m_projectMonitor->slotZoneStart();
0732     }
0733 }
0734 
0735 void MonitorManager::slotZoneEnd()
0736 {
0737     if (m_activeMonitor == m_projectMonitor) {
0738         m_projectMonitor->slotZoneEnd();
0739     } else if (m_activeMonitor == m_clipMonitor) {
0740         m_clipMonitor->slotZoneEnd();
0741     }
0742 }
0743 
0744 void MonitorManager::slotSetInPoint()
0745 {
0746     if (m_activeMonitor == m_clipMonitor) {
0747         m_clipMonitor->slotSetZoneStart();
0748     } else if (m_activeMonitor == m_projectMonitor) {
0749         QPoint sourceZone = m_projectMonitor->getZoneInfo();
0750         QPoint destZone = sourceZone;
0751         destZone.setX(m_projectMonitor->position());
0752         if (destZone.x() > destZone.y()) {
0753             destZone.setY(qMin(pCore->projectDuration(), destZone.x() + (sourceZone.y() - sourceZone.x())));
0754         }
0755         Q_EMIT m_projectMonitor->zoneUpdatedWithUndo(sourceZone, destZone);
0756     }
0757 }
0758 
0759 void MonitorManager::slotSetOutPoint()
0760 {
0761     if (m_activeMonitor == m_clipMonitor) {
0762         m_clipMonitor->slotSetZoneEnd();
0763     } else if (m_activeMonitor == m_projectMonitor) {
0764         QPoint sourceZone = m_projectMonitor->getZoneInfo();
0765         QPoint destZone = sourceZone;
0766         destZone.setY(m_projectMonitor->position() + 1);
0767         if (destZone.y() < destZone.x()) {
0768             destZone.setX(qMax(0, destZone.y() - (sourceZone.y() - sourceZone.x())));
0769         }
0770         Q_EMIT m_projectMonitor->zoneUpdatedWithUndo(sourceZone, destZone);
0771     }
0772 }
0773 
0774 void MonitorManager::slotExtractCurrentFrame()
0775 {
0776     if (m_activeMonitor) {
0777         static_cast<Monitor *>(m_activeMonitor)->slotExtractCurrentFrame();
0778     }
0779 }
0780 
0781 void MonitorManager::slotExtractCurrentFrameToProject()
0782 {
0783     if (m_activeMonitor) {
0784         static_cast<Monitor *>(m_activeMonitor)->slotExtractCurrentFrame(QString(), true);
0785     }
0786 }
0787 
0788 void MonitorManager::slotZoomIn()
0789 {
0790     if (m_activeMonitor) {
0791         static_cast<Monitor *>(m_activeMonitor)->slotZoomIn();
0792     }
0793 }
0794 
0795 void MonitorManager::slotZoomOut()
0796 {
0797     if (m_activeMonitor) {
0798         static_cast<Monitor *>(m_activeMonitor)->slotZoomOut();
0799     }
0800 }
0801 
0802 bool MonitorManager::isMultiTrack() const
0803 {
0804     if (m_multiTrack) {
0805         return m_multiTrack->isChecked();
0806     }
0807     return false;
0808 }
0809 
0810 void MonitorManager::switchMultiTrackView(bool enable)
0811 {
0812     if (isMultiTrack()) {
0813         if (!enable) {
0814             m_multiTrack->trigger();
0815         }
0816     } else if (enable) {
0817         m_multiTrack->trigger();
0818     }
0819 }
0820 
0821 bool MonitorManager::isTrimming() const
0822 {
0823     if (m_projectMonitor && m_projectMonitor->m_trimmingbar) {
0824         return m_projectMonitor->m_trimmingbar->isVisible();
0825     }
0826     return false;
0827 }
0828 
0829 void MonitorManager::updateBgColor()
0830 {
0831     if (m_projectMonitor) {
0832         m_projectMonitor->updateBgColor();
0833         m_projectMonitor->forceMonitorRefresh();
0834     }
0835     if (m_clipMonitor) {
0836         m_clipMonitor->updateBgColor();
0837         m_clipMonitor->forceMonitorRefresh();
0838     }
0839 }