File indexing completed on 2024-04-28 08:45:04

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 "mainwindow.h"
0008 #include "assets/assetpanel.hpp"
0009 #include "audiomixer/mixermanager.hpp"
0010 #include "bin/clipcreator.hpp"
0011 #include "bin/generators/generators.h"
0012 #include "bin/mediabrowser.h"
0013 #include "bin/model/subtitlemodel.hpp"
0014 #include "bin/projectclip.h"
0015 #include "bin/projectfolder.h"
0016 #include "bin/projectitemmodel.h"
0017 #include "core.h"
0018 #include "dialogs/clipcreationdialog.h"
0019 #include "dialogs/clipjobmanager.h"
0020 #include "dialogs/kdenlivesettingsdialog.h"
0021 #include "dialogs/renderwidget.h"
0022 #include "dialogs/subtitleedit.h"
0023 #include "dialogs/wizard.h"
0024 #include "doc/docundostack.hpp"
0025 #include "doc/kdenlivedoc.h"
0026 #include "docktitlebarmanager.h"
0027 #include "effects/effectbasket.h"
0028 #include "effects/effectlist/view/effectlistwidget.hpp"
0029 #include "jobs/audiolevelstask.h"
0030 #include "jobs/customjobtask.h"
0031 #include "jobs/scenesplittask.h"
0032 #include "jobs/speedtask.h"
0033 #include "jobs/stabilizetask.h"
0034 #include "jobs/transcodetask.h"
0035 #include "kdenlivesettings.h"
0036 #include "layoutmanagement.h"
0037 #include "library/librarywidget.h"
0038 #ifdef NODBUS
0039 #include "render/renderserver.h"
0040 #else
0041 #include "mainwindowadaptor.h"
0042 #endif
0043 #include "dialogs/textbasededit.h"
0044 #include "dialogs/timeremap.h"
0045 #include "lib/localeHandling.h"
0046 #include "mltconnection.h"
0047 #include "mltcontroller/clipcontroller.h"
0048 #include "monitor/monitor.h"
0049 #include "monitor/monitormanager.h"
0050 #include "monitor/scopes/audiographspectrum.h"
0051 #include "onlineresources/resourcewidget.hpp"
0052 #include "profiles/profilemodel.hpp"
0053 #include "profiles/profilerepository.hpp"
0054 #include "project/cliptranscode.h"
0055 #include "project/dialogs/archivewidget.h"
0056 #include "project/dialogs/guideslist.h"
0057 #include "project/dialogs/projectsettings.h"
0058 #include "project/dialogs/temporarydata.h"
0059 #include "project/projectmanager.h"
0060 #include "scopes/scopemanager.h"
0061 #include "timeline2/view/timelinecontroller.h"
0062 #include "timeline2/view/timelinetabs.hpp"
0063 #include "timeline2/view/timelinewidget.h"
0064 #include "titler/titlewidget.h"
0065 #include "transitions/transitionlist/view/transitionlistwidget.hpp"
0066 #include "transitions/transitionsrepository.hpp"
0067 #include "utils/thememanager.h"
0068 #include "widgets/progressbutton.h"
0069 #include <config-kdenlive.h>
0070 
0071 #ifdef USE_JOGSHUTTLE
0072 #include "jogshuttle/jogmanager.h"
0073 #endif
0074 
0075 #include "knewstuff_version.h"
0076 #include "kwidgetsaddons_version.h"
0077 #include "utils/KMessageBox_KdenliveCompat.h"
0078 #include <KAboutData>
0079 #include <KActionCollection>
0080 #include <KActionMenu>
0081 #include <KColorScheme>
0082 #include <KConfigDialog>
0083 #include <KCoreAddons>
0084 #include <KDualAction>
0085 #include <KEditToolBar>
0086 #include <KIconTheme>
0087 #include <KLocalizedString>
0088 #include <KMessageBox>
0089 #if KNEWSTUFF_VERSION >= QT_VERSION_CHECK(5, 240, 0)
0090 #include <KNSWidgets/Dialog>
0091 #else
0092 #include <KNS3/QtQuickDialogWrapper>
0093 #endif
0094 #include <KNotifyConfigWidget>
0095 #include <KRecentDirs>
0096 #include <KShortcutsDialog>
0097 #include <KStandardAction>
0098 #include <KToggleFullScreenAction>
0099 #include <KToolBar>
0100 #include <KXMLGUIFactory>
0101 
0102 #include <KConfigGroup>
0103 #include <QAction>
0104 #include <QClipboard>
0105 #include <QDesktopServices>
0106 #include <QDialogButtonBox>
0107 #include <QFileDialog>
0108 #include <QMenu>
0109 #include <QMenuBar>
0110 #include <QPushButton>
0111 #include <QScreen>
0112 #include <QStandardPaths>
0113 #include <QStatusBar>
0114 #include <QStyleFactory>
0115 #include <QUndoGroup>
0116 #include <QVBoxLayout>
0117 
0118 static const char version[] = KDENLIVE_VERSION;
0119 namespace Mlt {
0120 class Producer;
0121 }
0122 
0123 QMap<QString, QImage> MainWindow::m_lumacache;
0124 QMap<QString, QStringList> MainWindow::m_lumaFiles;
0125 
0126 /*static bool sortByNames(const QPair<QString, QAction *> &a, const QPair<QString, QAction*> &b)
0127 {
0128     return a.first < b.first;
0129 }*/
0130 
0131 // determine the default KDE style as defined BY THE USER
0132 // (as opposed to whatever style KDE considers default)
0133 static QString defaultStyle(const char *fallback = nullptr)
0134 {
0135     KSharedConfigPtr kdeGlobals = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::NoGlobals);
0136     KConfigGroup cg(kdeGlobals, "KDE");
0137     return cg.readEntry("widgetStyle", fallback);
0138 }
0139 
0140 MainWindow::MainWindow(QWidget *parent)
0141     : KXmlGuiWindow(parent)
0142     , m_activeTool(ToolType::SelectTool)
0143     , m_mousePosition(0)
0144     , m_effectBasket(nullptr)
0145 {
0146     // Init all action categories that are used by other parts of the software
0147     // before we call MainWindow::init and therefore can't be initialized there
0148     KActionCategory *category = new KActionCategory(i18n("Monitor"), actionCollection());
0149     kdenliveCategoryMap.insert(QStringLiteral("monitor"), category);
0150     category = new KActionCategory(i18n("Add Clip"), actionCollection());
0151     kdenliveCategoryMap.insert(QStringLiteral("addclip"), category);
0152     category = new KActionCategory(i18n("Navigation and Playback"), actionCollection());
0153     kdenliveCategoryMap.insert(QStringLiteral("navandplayback"), category);
0154     category = new KActionCategory(i18n("Bin Tags"), actionCollection());
0155     kdenliveCategoryMap.insert(QStringLiteral("bintags"), category);
0156 }
0157 
0158 void MainWindow::init(const QString &mltPath)
0159 {
0160     QString desktopStyle = QApplication::style()->objectName();
0161     // Load themes
0162     auto themeManager = new ThemeManager(actionCollection());
0163     actionCollection()->addAction(QStringLiteral("themes_menu"), themeManager->menu());
0164     connect(themeManager, &ThemeManager::themeChanged, this, &MainWindow::slotThemeChanged);
0165     Q_EMIT pCore->updatePalette();
0166 
0167     if (!KdenliveSettings::widgetstyle().isEmpty() && QString::compare(desktopStyle, KdenliveSettings::widgetstyle(), Qt::CaseInsensitive) != 0) {
0168         // User wants a custom widget style, init
0169         doChangeStyle();
0170     }
0171 
0172     // Widget themes for non KDE users
0173     KActionMenu *stylesAction = new KActionMenu(i18n("Style"), this);
0174     auto *stylesGroup = new QActionGroup(stylesAction);
0175 
0176     // GTK theme does not work well with Kdenlive, and does not support color theming, so avoid it
0177     QStringList availableStyles = QStyleFactory::keys();
0178     if (KdenliveSettings::widgetstyle().isEmpty()) {
0179         // First run
0180         QStringList incompatibleStyles = {QStringLiteral("GTK+"), QStringLiteral("windowsvista"), QStringLiteral("Windows"), QStringLiteral("macintosh")};
0181 
0182         if (incompatibleStyles.contains(desktopStyle, Qt::CaseInsensitive)) {
0183             if (availableStyles.contains(QStringLiteral("breeze"), Qt::CaseInsensitive)) {
0184                 // Auto switch to Breeze theme
0185                 KdenliveSettings::setWidgetstyle(QStringLiteral("Breeze"));
0186                 QApplication::setStyle(QStyleFactory::create(QStringLiteral("Breeze")));
0187             } else if (availableStyles.contains(QStringLiteral("fusion"), Qt::CaseInsensitive)) {
0188                 KdenliveSettings::setWidgetstyle(QStringLiteral("Fusion"));
0189                 QApplication::setStyle(QStyleFactory::create(QStringLiteral("Fusion")));
0190             }
0191         } else {
0192             KdenliveSettings::setWidgetstyle(QStringLiteral("Default"));
0193         }
0194     }
0195 
0196     // Add default style action
0197     QAction *defaultStyle = new QAction(i18n("Default"), stylesGroup);
0198     defaultStyle->setData(QStringLiteral("Default"));
0199     defaultStyle->setCheckable(true);
0200     stylesAction->addAction(defaultStyle);
0201     if (KdenliveSettings::widgetstyle() == QLatin1String("Default") || KdenliveSettings::widgetstyle().isEmpty()) {
0202         defaultStyle->setChecked(true);
0203     }
0204 
0205     for (const QString &style : qAsConst(availableStyles)) {
0206         auto *a = new QAction(style, stylesGroup);
0207         a->setCheckable(true);
0208         a->setData(style);
0209         if (KdenliveSettings::widgetstyle() == style) {
0210             a->setChecked(true);
0211         }
0212         stylesAction->addAction(a);
0213     }
0214     connect(stylesGroup, &QActionGroup::triggered, this, &MainWindow::slotChangeStyle);
0215     // QIcon::setThemeSearchPaths(QStringList() <<QStringLiteral(":/icons/"));
0216 #ifdef NODBUS
0217     new RenderServer(this);
0218 #else
0219     new RenderingAdaptor(this);
0220 #endif
0221     QString defaultProfile = KdenliveSettings::default_profile();
0222 
0223     // Initialise MLT connection
0224     MltConnection::construct(mltPath);
0225     pCore->setCurrentProfile(defaultProfile.isEmpty() ? ProjectManager::getDefaultProjectFormat() : defaultProfile);
0226     m_commandStack = new QUndoGroup();
0227 
0228     // If using a custom profile, make sure the file exists or fallback to default
0229     QString currentProfilePath = pCore->getCurrentProfile()->path();
0230     if (currentProfilePath.startsWith(QLatin1Char('/')) && !QFile::exists(currentProfilePath)) {
0231         KMessageBox::error(this, i18n("Cannot find your default profile, switching to ATSC 1080p 25"));
0232         pCore->setCurrentProfile(QStringLiteral("atsc_1080p_25"));
0233         KdenliveSettings::setDefault_profile(QStringLiteral("atsc_1080p_25"));
0234     }
0235 
0236     // Disable movit until it's stable
0237     m_gpuAllowed = false;
0238     KdenliveSettings::setGpu_accel(false);
0239 
0240     // m_gpuAllowed = EffectsRepository::get()->hasInternalEffect(QStringLiteral("glsl.manager"));
0241 
0242     m_shortcutRemoveFocus = new QShortcut(QKeySequence(QStringLiteral("Esc")), this);
0243     connect(m_shortcutRemoveFocus, &QShortcut::activated, this, &MainWindow::slotRemoveFocus);
0244 
0245     /// Add Widgets
0246     setDockOptions(dockOptions() | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
0247     setDockOptions(dockOptions() | QMainWindow::GroupedDragging);
0248     setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition(KdenliveSettings::tabposition()));
0249     m_timelineToolBar = toolBar(QStringLiteral("timelineToolBar"));
0250     m_timelineToolBarContainer = new TimelineContainer(this);
0251     auto *ctnLay = new QVBoxLayout;
0252     ctnLay->setSpacing(0);
0253     ctnLay->setContentsMargins(0, 0, 0, 0);
0254     m_timelineToolBarContainer->setLayout(ctnLay);
0255     QFrame *topFrame = new QFrame(this);
0256     topFrame->setFrameShape(QFrame::HLine);
0257     topFrame->setFixedHeight(1);
0258     topFrame->setLineWidth(1);
0259     connect(this, &MainWindow::showTimelineFocus, this, [topFrame](bool focus, bool highlight) {
0260         if (focus) {
0261             KColorScheme scheme(QApplication::palette().currentColorGroup(), KColorScheme::Tooltip);
0262             if (highlight) {
0263                 QColor col = scheme.decoration(KColorScheme::HoverColor).color();
0264                 topFrame->setStyleSheet(QString("QFrame {border: 1px solid rgba(%1,%2,%3,70)}").arg(col.red()).arg(col.green()).arg(col.blue()));
0265             } else {
0266                 QColor col = scheme.decoration(KColorScheme::FocusColor).color();
0267                 topFrame->setStyleSheet(QString("QFrame {border: 1px solid rgba(%1,%2,%3,100)}").arg(col.red()).arg(col.green()).arg(col.blue()));
0268             }
0269         } else {
0270             topFrame->setStyleSheet(QString());
0271         }
0272     });
0273     ctnLay->addWidget(topFrame);
0274     ctnLay->addWidget(m_timelineToolBar);
0275     KSharedConfigPtr config = KSharedConfig::openConfig();
0276     KConfigGroup mainConfig(config, QStringLiteral("MainWindow"));
0277     KConfigGroup tbGroup(&mainConfig, QStringLiteral("Toolbar timelineToolBar"));
0278     m_timelineToolBar->applySettings(tbGroup);
0279     QFrame *fr = new QFrame(this);
0280     fr->setFrameShape(QFrame::HLine);
0281     fr->setMaximumHeight(1);
0282     fr->setLineWidth(1);
0283     ctnLay->addWidget(fr);
0284     setupActions();
0285     auto *layoutManager = new LayoutManagement(this);
0286     pCore->bin()->setupMenu();
0287     pCore->buildDocks();
0288 
0289     QDockWidget *libraryDock = addDock(i18n("Library"), QStringLiteral("library"), pCore->library());
0290     QDockWidget *subtitlesDock = addDock(i18n("Subtitles"), QStringLiteral("Subtitles"), pCore->subtitleWidget());
0291     QDockWidget *textEditingDock = addDock(i18n("Speech Editor"), QStringLiteral("textedit"), pCore->textEditWidget());
0292     QDockWidget *timeRemapDock = addDock(i18n("Time Remapping"), QStringLiteral("timeremap"), pCore->timeRemapWidget());
0293     QDockWidget *guidesDock = addDock(i18n("Guides"), QStringLiteral("guides"), pCore->guidesList());
0294     connect(pCore.get(), &Core::remapClip, this, [&, timeRemapDock](int id) {
0295         if (id > -1) {
0296             timeRemapDock->show();
0297             timeRemapDock->raise();
0298         }
0299         pCore->timeRemapWidget()->selectedClip(id, pCore->currentTimelineId());
0300     });
0301     m_clipMonitor = new Monitor(Kdenlive::ClipMonitor, pCore->monitorManager(), this);
0302     pCore->bin()->setMonitor(m_clipMonitor);
0303     connect(m_clipMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
0304     connect(m_clipMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteClipMarker);
0305     connect(m_clipMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
0306     connect(m_clipMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
0307     connect(m_clipMonitor, &Monitor::passKeyPress, this, &MainWindow::triggerKey);
0308 
0309     m_projectMonitor = new Monitor(Kdenlive::ProjectMonitor, pCore->monitorManager(), this);
0310     connect(m_projectMonitor, &Monitor::passKeyPress, this, &MainWindow::triggerKey);
0311     connect(m_projectMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
0312     connect(m_projectMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteGuide);
0313     connect(m_projectMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
0314     connect(m_projectMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
0315     connect(m_loopClip, &QAction::triggered, this, [&]() {
0316         QPoint inOut = getCurrentTimeline()->controller()->selectionInOut();
0317         m_projectMonitor->slotLoopClip(inOut);
0318     });
0319     installEventFilter(this);
0320     pCore->monitorManager()->initMonitors(m_clipMonitor, m_projectMonitor);
0321 
0322     m_timelineTabs = new TimelineTabs(this);
0323     ctnLay->addWidget(m_timelineTabs);
0324     setCentralWidget(m_timelineToolBarContainer);
0325 
0326     // Screen grab widget
0327     QWidget *grabWidget = new QWidget(this);
0328     auto *grabLayout = new QVBoxLayout;
0329     grabWidget->setLayout(grabLayout);
0330     auto *recToolbar = new QToolBar(grabWidget);
0331     grabLayout->addWidget(recToolbar);
0332     grabLayout->addStretch(10);
0333     // Check number of monitors for FFmpeg screen capture
0334     int screens = QApplication::screens().count();
0335     if (screens > 1) {
0336         auto *screenCombo = new QComboBox(recToolbar);
0337         for (int ix = 0; ix < screens; ix++) {
0338             screenCombo->addItem(i18n("Monitor %1", ix));
0339         }
0340         connect(screenCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), m_clipMonitor, &Monitor::slotSetScreen);
0341         recToolbar->addWidget(screenCombo);
0342         // Update screen grab monitor choice in case we changed from fullscreen
0343         screenCombo->setEnabled(KdenliveSettings::grab_capture_type() == 0);
0344     }
0345     QAction *recAction = m_clipMonitor->recAction();
0346     addAction(QStringLiteral("screengrab_record"), recAction);
0347     recToolbar->addAction(recAction);
0348     QAction *recConfig = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure Recording"), this);
0349     recToolbar->addAction(recConfig);
0350     connect(recConfig, &QAction::triggered, [&]() { Q_EMIT pCore->showConfigDialog(Kdenlive::PageCapture, 0); });
0351     QDockWidget *screenGrabDock = addDock(i18n("Screen Grab"), QStringLiteral("screengrab"), grabWidget);
0352 
0353     // Sequence actions
0354     QAction *nextSequence = new QAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Switch to next Sequence"), this);
0355     addAction(QStringLiteral("sequence_next"), nextSequence, QKeySequence::NextChild);
0356     connect(nextSequence, &QAction::triggered, m_timelineTabs, &TimelineTabs::slotNextSequence);
0357 
0358     QAction *prevSequence = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Switch to previous Sequence"), this);
0359     addAction(QStringLiteral("sequence_previous"), prevSequence, QKeySequence::PreviousChild);
0360     connect(prevSequence, &QAction::triggered, m_timelineTabs, &TimelineTabs::slotPreviousSequence);
0361 
0362     // Audio spectrum scope
0363     m_audioSpectrum = new AudioGraphSpectrum(pCore->monitorManager());
0364     QDockWidget *spectrumDock = addDock(i18n("Audio Spectrum"), QStringLiteral("audiospectrum"), m_audioSpectrum);
0365     connect(spectrumDock, &QDockWidget::visibilityChanged, this, [&](bool visible) { m_audioSpectrum->dockVisible(visible); });
0366 
0367     // Project bin
0368     m_projectBinDock = addDock(i18n("Project Bin"), QStringLiteral("project_bin"), pCore->bin());
0369 
0370     // Media browser widget
0371     QDockWidget *clipDockWidget = addDock(i18n("Media Browser"), QStringLiteral("bin_clip"), pCore->mediaBrowser());
0372 
0373     // Online resources widget
0374     auto *onlineResources = new ResourceWidget(this);
0375     m_onlineResourcesDock = addDock(i18n("Online Resources"), QStringLiteral("onlineresources"), onlineResources);
0376     connect(onlineResources, &ResourceWidget::previewClip, this, [&](const QString &path, const QString &title) {
0377         m_clipMonitor->slotPreviewResource(path, title);
0378         m_clipMonitorDock->show();
0379         m_clipMonitorDock->raise();
0380     });
0381 
0382     connect(onlineResources, &ResourceWidget::addClip, this, &MainWindow::slotAddProjectClip);
0383     connect(onlineResources, &ResourceWidget::addLicenseInfo, this, &MainWindow::slotAddTextNote);
0384 
0385     // Close library and audiospectrum and others on first run
0386     screenGrabDock->close();
0387     libraryDock->close();
0388     subtitlesDock->close();
0389     textEditingDock->close();
0390     timeRemapDock->close();
0391     spectrumDock->close();
0392     clipDockWidget->close();
0393     guidesDock->close();
0394     m_onlineResourcesDock->close();
0395 
0396     m_effectStackDock = addDock(i18n("Effect/Composition Stack"), QStringLiteral("effect_stack"), m_assetPanel);
0397     connect(m_assetPanel, &AssetPanel::doSplitEffect, m_projectMonitor, &Monitor::slotSwitchCompare);
0398     connect(m_assetPanel, &AssetPanel::doSplitBinEffect, m_clipMonitor, &Monitor::slotSwitchCompare);
0399     connect(m_assetPanel, &AssetPanel::switchCurrentComposition, this,
0400             [&](int cid, const QString &compositionId) { getCurrentTimeline()->model()->switchComposition(cid, compositionId); });
0401     connect(pCore->bin(), &Bin::updateTabName, m_timelineTabs, &TimelineTabs::renameTab);
0402     connect(m_timelineTabs, &TimelineTabs::showMixModel, m_assetPanel, &AssetPanel::showMix);
0403     connect(m_timelineTabs, &TimelineTabs::showTransitionModel, this, [&](int tid, std::shared_ptr<AssetParameterModel> model) {
0404         m_assetPanel->showTransition(tid, model);
0405         m_effectStackDock->raise();
0406     });
0407     connect(m_timelineTabs, &TimelineTabs::showItemEffectStack, this,
0408             [&](const QString &clipName, std::shared_ptr<EffectStackModel> model, QSize size, bool showKeyframes) {
0409                 if (model == nullptr && m_assetPanel->effectStackOwner().type == KdenliveObjectType::BinClip) {
0410                     // Effect stask is currently displaying a bin clip, do nothing
0411                     return;
0412                 }
0413                 m_assetPanel->showEffectStack(clipName, model, size, showKeyframes);
0414                 m_effectStackDock->raise();
0415             });
0416 
0417     connect(m_timelineTabs, &TimelineTabs::updateAssetPosition, m_assetPanel, &AssetPanel::updateAssetPosition);
0418 
0419     connect(m_timelineTabs, &TimelineTabs::showSubtitle, this, [&, subtitlesDock](int id) {
0420         if (id > -1) {
0421             subtitlesDock->show();
0422             subtitlesDock->raise();
0423         }
0424         pCore->subtitleWidget()->setActiveSubtitle(id);
0425     });
0426 
0427     connect(m_timelineTabs, &TimelineTabs::updateZoom, this, &MainWindow::updateZoomSlider);
0428     connect(pCore->bin(), &Bin::requestShowEffectStack, [&]() {
0429         // Don't raise effect stack on clip bin in case it is docked with bin or clip monitor
0430         // m_effectStackDock->raise();
0431     });
0432     connect(this, &MainWindow::clearAssetPanel, m_assetPanel, &AssetPanel::clearAssetPanel, Qt::DirectConnection);
0433     connect(this, &MainWindow::assetPanelWarning, m_assetPanel, &AssetPanel::assetPanelWarning);
0434     connect(m_assetPanel, &AssetPanel::seekToPos, this, [this](int pos) {
0435         ObjectId oId = m_assetPanel->effectStackOwner();
0436         switch (oId.type) {
0437         case KdenliveObjectType::TimelineTrack:
0438         case KdenliveObjectType::TimelineClip:
0439         case KdenliveObjectType::TimelineComposition:
0440         case KdenliveObjectType::Master:
0441         case KdenliveObjectType::TimelineMix:
0442             m_projectMonitor->requestSeek(pos);
0443             break;
0444         case KdenliveObjectType::BinClip:
0445             m_clipMonitor->requestSeek(pos);
0446             break;
0447         default:
0448             qDebug() << "ERROR unhandled object type";
0449             break;
0450         }
0451     });
0452 
0453     m_effectList2 = new EffectListWidget(this);
0454     connect(m_effectList2, &EffectListWidget::activateAsset, pCore->projectManager(), &ProjectManager::activateAsset);
0455     connect(m_assetPanel, &AssetPanel::reloadEffect, m_effectList2, &EffectListWidget::reloadCustomEffect);
0456     m_effectListDock = addDock(i18n("Effects"), QStringLiteral("effect_list"), m_effectList2);
0457 
0458     m_compositionList = new TransitionListWidget(this);
0459     m_compositionListDock = addDock(i18n("Compositions"), QStringLiteral("transition_list"), m_compositionList);
0460 
0461     // Add monitors here to keep them at the right of the window
0462     m_clipMonitorDock = addDock(i18n("Clip Monitor"), QStringLiteral("clip_monitor"), m_clipMonitor);
0463     m_projectMonitorDock = addDock(i18n("Project Monitor"), QStringLiteral("project_monitor"), m_projectMonitor);
0464 
0465     m_undoView = new QUndoView();
0466     m_undoView->setCleanIcon(QIcon::fromTheme(QStringLiteral("edit-clear")));
0467     m_undoView->setEmptyLabel(i18n("Clean"));
0468     m_undoView->setGroup(m_commandStack);
0469     m_undoViewDock = addDock(i18n("Undo History"), QStringLiteral("undo_history"), m_undoView);
0470 
0471     // Color and icon theme stuff
0472     connect(m_commandStack, &QUndoGroup::cleanChanged, m_saveAction, &QAction::setDisabled);
0473     addAction(QStringLiteral("styles_menu"), stylesAction);
0474 
0475     QAction *iconAction = new QAction(i18n("Force Breeze Icon Theme"), this);
0476     iconAction->setCheckable(true);
0477     iconAction->setChecked(KdenliveSettings::force_breeze());
0478     addAction(QStringLiteral("force_icon_theme"), iconAction);
0479     connect(iconAction, &QAction::triggered, this, &MainWindow::forceIconSet);
0480 
0481     m_mixerDock = addDock(i18n("Audio Mixer"), QStringLiteral("mixer"), pCore->mixer());
0482     m_mixerDock->setWhatsThis(xi18nc("@info:whatsthis", "Toggles the audio mixer panel/widget."));
0483     QAction *showMixer = new QAction(QIcon::fromTheme(QStringLiteral("view-media-equalizer")), i18n("Audio Mixer"), this);
0484     showMixer->setCheckable(true);
0485     addAction(QStringLiteral("audiomixer_button"), showMixer);
0486     connect(m_mixerDock, &QDockWidget::visibilityChanged, this, [&, showMixer](bool visible) {
0487         pCore->mixer()->connectMixer(visible);
0488         pCore->audioMixerVisible = visible;
0489         m_projectMonitor->displayAudioMonitor(m_projectMonitor->isActive());
0490         showMixer->setChecked(visible);
0491     });
0492     connect(showMixer, &QAction::triggered, this, [&]() {
0493         if (m_mixerDock->isVisible() && !m_mixerDock->visibleRegion().isEmpty()) {
0494             m_mixerDock->close();
0495         } else {
0496             m_mixerDock->show();
0497             m_mixerDock->raise();
0498         }
0499     });
0500 
0501     // Close non-general docks for the initial layout
0502     // only show important ones
0503     m_undoViewDock->close();
0504     m_mixerDock->close();
0505 
0506     // Tabify Widgets
0507     tabifyDockWidget(m_clipMonitorDock, m_projectMonitorDock);
0508     tabifyDockWidget(m_compositionListDock, m_effectListDock);
0509     tabifyDockWidget(m_effectStackDock, pCore->bin()->clipPropertiesDock());
0510     bool firstRun = readOptions();
0511     if (KdenliveSettings::lastCacheCheck().isNull()) {
0512         // Define a date for first check
0513         KdenliveSettings::setLastCacheCheck(QDateTime::currentDateTime());
0514     }
0515 
0516     // Build effects menu
0517     m_effectsMenu = new QMenu(i18n("Add Effect"), this);
0518     m_effectActions = new KActionCategory(i18n("Effects"), actionCollection());
0519     m_effectList2->reloadEffectMenu(m_effectsMenu, m_effectActions);
0520 
0521     m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
0522     m_transitionActions = new KActionCategory(i18n("Transitions"), actionCollection());
0523 
0524     auto *scmanager = new ScopeManager(this);
0525 
0526     auto *titleBars = new DockTitleBarManager(this);
0527     connect(layoutManager, &LayoutManagement::updateTitleBars, titleBars, [&]() { titleBars->slotUpdateTitleBars(); });
0528     connect(layoutManager, &LayoutManagement::connectDocks, titleBars, &DockTitleBarManager::connectDocks);
0529     m_extraFactory = new KXMLGUIClient(this);
0530     buildDynamicActions();
0531 
0532     // Create Effect Basket (dropdown list of favorites)
0533     m_effectBasket = new EffectBasket(this);
0534     connect(m_effectBasket, &EffectBasket::activateAsset, pCore->projectManager(), &ProjectManager::activateAsset);
0535     connect(m_effectList2, &EffectListWidget::reloadFavorites, m_effectBasket, &EffectBasket::slotReloadBasket);
0536     auto *widgetlist = new QWidgetAction(this);
0537     widgetlist->setDefaultWidget(m_effectBasket);
0538     // widgetlist->setText(i18n("Favorite Effects"));
0539     widgetlist->setToolTip(i18n("Favorite Effects"));
0540     widgetlist->setWhatsThis(xi18nc("@info:whatsthis", "Click to show a list of favorite effects. Double-click on an effect to add it to the selected clip."));
0541     widgetlist->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
0542     auto *menu = new QMenu(this);
0543     menu->addAction(widgetlist);
0544 
0545     auto *basketButton = new QToolButton(this);
0546     basketButton->setMenu(menu);
0547     basketButton->setToolButtonStyle(toolBar()->toolButtonStyle());
0548     basketButton->setDefaultAction(widgetlist);
0549     basketButton->setPopupMode(QToolButton::InstantPopup);
0550     // basketButton->setText(i18n("Favorite Effects"));
0551     basketButton->setToolTip(i18n("Favorite Effects"));
0552     basketButton->setWhatsThis(
0553         xi18nc("@info:whatsthis", "Click to show a list of favorite effects. Double-click on an effect to add it to the selected clip."));
0554 
0555     basketButton->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
0556 
0557     auto *toolButtonAction = new QWidgetAction(this);
0558     toolButtonAction->setText(i18n("Favorite Effects"));
0559     toolButtonAction->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
0560     toolButtonAction->setDefaultWidget(basketButton);
0561     addAction(QStringLiteral("favorite_effects"), toolButtonAction);
0562     connect(toolButtonAction, &QAction::triggered, basketButton, &QToolButton::showMenu);
0563     connect(m_effectBasket, &EffectBasket::activateAsset, menu, &QMenu::close);
0564 
0565     // Render button
0566     ProgressButton *timelineRender = new ProgressButton(i18n("Render…"), 100, this);
0567     auto *tlrMenu = new QMenu(this);
0568     timelineRender->setMenu(tlrMenu);
0569     connect(this, &MainWindow::setRenderProgress, timelineRender, &ProgressButton::setProgress);
0570     auto *renderButtonAction = new QWidgetAction(this);
0571     renderButtonAction->setText(i18n("Render Button"));
0572     renderButtonAction->setIcon(QIcon::fromTheme(QStringLiteral("media-record")));
0573     renderButtonAction->setDefaultWidget(timelineRender);
0574     addAction(QStringLiteral("project_render_button"), renderButtonAction);
0575 
0576     // Timeline preview button
0577     ProgressButton *timelinePreview = new ProgressButton(i18n("Rendering preview"), 1000, this);
0578     auto *tlMenu = new QMenu(this);
0579     timelinePreview->setMenu(tlMenu);
0580     connect(this, &MainWindow::setPreviewProgress, timelinePreview, &ProgressButton::setProgress);
0581     auto *previewButtonAction = new QWidgetAction(this);
0582     previewButtonAction->setText(i18n("Timeline Preview"));
0583     previewButtonAction->setIcon(QIcon::fromTheme(QStringLiteral("preview-render-on")));
0584     previewButtonAction->setDefaultWidget(timelinePreview);
0585     addAction(QStringLiteral("timeline_preview_button"), previewButtonAction);
0586     setupGUI(KXmlGuiWindow::ToolBar | KXmlGuiWindow::StatusBar | KXmlGuiWindow::Save | KXmlGuiWindow::Create);
0587 
0588     LocaleHandling::resetLocale();
0589     if (firstRun) {
0590         if (QScreen *current = QApplication::primaryScreen()) {
0591             int screenHeight = current->availableSize().height();
0592             if (screenHeight < 1000) {
0593                 resize(current->availableSize());
0594             } else if (screenHeight < 2000) {
0595                 resize(current->availableSize() / 1.2);
0596             } else {
0597                 resize(current->availableSize() / 1.6);
0598             }
0599         }
0600     }
0601 
0602     m_timelineToolBar->setToolButtonStyle(Qt::ToolButtonFollowStyle);
0603     m_timelineToolBar->setProperty("otherToolbar", true);
0604     timelinePreview->setToolButtonStyle(m_timelineToolBar->toolButtonStyle());
0605     connect(m_timelineToolBar, &QToolBar::toolButtonStyleChanged, timelinePreview, &ProgressButton::setToolButtonStyle);
0606 
0607     timelineRender->setToolButtonStyle(toolBar()->toolButtonStyle());
0608     /*ScriptingPart* sp = new ScriptingPart(this, QStringList());
0609     guiFactory()->addClient(sp);*/
0610 
0611     loadDockActions();
0612     loadClipActions();
0613     loadContainerActions();
0614 
0615     // Timeline composition menu
0616     auto *compositionMenu = new QMenu(this);
0617     compositionMenu->addAction(actionCollection()->action(QStringLiteral("edit_item_duration")));
0618     compositionMenu->addAction(actionCollection()->action(QStringLiteral("edit_copy")));
0619     compositionMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
0620 
0621     // Timeline main menu
0622     auto *timelineMenu = new QMenu(this);
0623     timelineMenu->addAction(actionCollection()->action(QStringLiteral("edit_paste")));
0624     timelineMenu->addAction(actionCollection()->action(QStringLiteral("insert_space")));
0625     timelineMenu->addAction(actionCollection()->action(QStringLiteral("delete_space")));
0626     timelineMenu->addAction(actionCollection()->action(QStringLiteral("delete_space_all_tracks")));
0627     timelineMenu->addAction(actionCollection()->action(QStringLiteral("add_guide")));
0628     timelineMenu->addAction(actionCollection()->action(QStringLiteral("edit_guide")));
0629     QMenu *guideMenu = new QMenu(i18n("Go to Guide…"), this);
0630     timelineMenu->addMenu(guideMenu);
0631 
0632     // Timeline ruler menu
0633     auto *timelineRulerMenu = new QMenu(this);
0634     timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("add_guide")));
0635     timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("edit_guide")));
0636     timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("lock_guides")));
0637     timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("export_guides")));
0638     timelineRulerMenu->addMenu(guideMenu);
0639     timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("mark_in")));
0640     timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("mark_out")));
0641     timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("select_timeline_zone")));
0642     timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("add_project_note")));
0643     timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("add_subtitle")));
0644 
0645     // Timeline subtitle menu
0646     auto *timelineSubtitleMenu = new QMenu(this);
0647     timelineSubtitleMenu->addAction(actionCollection()->action(QStringLiteral("edit_copy")));
0648     timelineSubtitleMenu->addAction(actionCollection()->action(QStringLiteral("delete_subtitle_clip")));
0649 
0650     // Timeline headers menu
0651     auto *timelineHeadersMenu = new QMenu(this);
0652     timelineHeadersMenu->addAction(actionCollection()->action(QStringLiteral("insert_track")));
0653     timelineHeadersMenu->addAction(actionCollection()->action(QStringLiteral("delete_track")));
0654     timelineHeadersMenu->addAction(actionCollection()->action(QStringLiteral("fit_all_tracks")));
0655     timelineHeadersMenu->addAction(actionCollection()->action(QStringLiteral("show_track_record")));
0656 
0657     QAction *separate_channels = new QAction(QIcon(), i18n("Separate Channels"), this);
0658     separate_channels->setCheckable(true);
0659     separate_channels->setChecked(KdenliveSettings::displayallchannels());
0660     separate_channels->setData("separate_channels");
0661     connect(separate_channels, &QAction::triggered, this, &MainWindow::slotSeparateAudioChannel);
0662     timelineHeadersMenu->addAction(separate_channels);
0663 
0664     QAction *normalize_channels = new QAction(QIcon(), i18n("Normalize Audio Thumbnails"), this);
0665     normalize_channels->setCheckable(true);
0666     normalize_channels->setChecked(KdenliveSettings::normalizechannels());
0667     normalize_channels->setData("normalize_channels");
0668     connect(normalize_channels, &QAction::triggered, this, &MainWindow::slotNormalizeAudioChannel);
0669     timelineHeadersMenu->addAction(normalize_channels);
0670 
0671     QMenu *thumbsMenu = new QMenu(i18n("Thumbnails"), this);
0672     auto *thumbGroup = new QActionGroup(this);
0673     QAction *inFrame = new QAction(i18n("In Frame"), thumbGroup);
0674     inFrame->setData(QStringLiteral("2"));
0675     inFrame->setCheckable(true);
0676     thumbsMenu->addAction(inFrame);
0677     QAction *inOutFrame = new QAction(i18n("In/Out Frames"), thumbGroup);
0678     inOutFrame->setData(QStringLiteral("0"));
0679     inOutFrame->setCheckable(true);
0680     thumbsMenu->addAction(inOutFrame);
0681     QAction *allFrame = new QAction(i18n("All Frames"), thumbGroup);
0682     allFrame->setData(QStringLiteral("1"));
0683     allFrame->setCheckable(true);
0684     thumbsMenu->addAction(allFrame);
0685     QAction *noFrame = new QAction(i18n("No Thumbnails"), thumbGroup);
0686     noFrame->setData(QStringLiteral("3"));
0687     noFrame->setCheckable(true);
0688     thumbsMenu->addAction(noFrame);
0689     pCore->bin()->setupGeneratorMenu();
0690 
0691     connect(pCore->monitorManager(), &MonitorManager::updateOverlayInfos, this, &MainWindow::slotUpdateMonitorOverlays);
0692 
0693     // Setup and fill effects and transitions menus.
0694     connect(m_effectsMenu, &QMenu::triggered, this, &MainWindow::slotAddEffect);
0695     connect(m_transitionsMenu, &QMenu::triggered, this, &MainWindow::slotAddTransition);
0696 
0697     m_timelineContextMenu = new QMenu(this);
0698 
0699     m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("insert_space")));
0700     m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("delete_space")));
0701     m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("delete_space_all_tracks")));
0702     m_timelineContextMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Paste)));
0703     slotConnectMonitors();
0704 
0705     m_timelineToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
0706     // TODO: let user select timeline toolbar toolbutton style
0707     // connect(toolBar(), &QToolBar::iconSizeChanged, m_timelineToolBar, &QToolBar::setToolButtonStyle);
0708     m_timelineToolBar->setContextMenuPolicy(Qt::CustomContextMenu);
0709     connect(m_timelineToolBar, &QWidget::customContextMenuRequested, this, &MainWindow::showTimelineToolbarMenu);
0710 
0711     QAction *prevRender = actionCollection()->action(QStringLiteral("prerender_timeline_zone"));
0712     QAction *stopPrevRender = actionCollection()->action(QStringLiteral("stop_prerender_timeline"));
0713     tlMenu->addAction(stopPrevRender);
0714     tlMenu->addAction(actionCollection()->action(QStringLiteral("set_render_timeline_zone")));
0715     tlMenu->addAction(actionCollection()->action(QStringLiteral("unset_render_timeline_zone")));
0716     tlMenu->addAction(actionCollection()->action(QStringLiteral("clear_render_timeline_zone")));
0717 
0718     // Automatic timeline preview action
0719     QAction *proxyRender = new QAction(i18n("Preview Using Proxy Clips"), this);
0720     proxyRender->setCheckable(true);
0721     proxyRender->setChecked(KdenliveSettings::proxypreview());
0722     connect(proxyRender, &QAction::triggered, this, [&](bool checked) { KdenliveSettings::setProxypreview(checked); });
0723     tlMenu->addAction(proxyRender);
0724 
0725     // Automatic timeline preview action
0726     QAction *autoRender = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Automatic Preview"), this);
0727     autoRender->setCheckable(true);
0728     autoRender->setChecked(KdenliveSettings::autopreview());
0729     connect(autoRender, &QAction::triggered, this, &MainWindow::slotToggleAutoPreview);
0730     tlMenu->addAction(autoRender);
0731     tlMenu->addSeparator();
0732     tlMenu->addAction(actionCollection()->action(QStringLiteral("disable_preview")));
0733     tlMenu->addAction(actionCollection()->action(QStringLiteral("manage_cache")));
0734     timelinePreview->defineDefaultAction(prevRender, stopPrevRender);
0735     timelinePreview->setAutoRaise(true);
0736 
0737     QAction *showRender = actionCollection()->action(QStringLiteral("project_render"));
0738     tlrMenu->addAction(showRender);
0739     tlrMenu->addAction(actionCollection()->action(QStringLiteral("stop_project_render")));
0740     timelineRender->defineDefaultAction(showRender, showRender);
0741     timelineRender->setAutoRaise(true);
0742 
0743     // Populate encoding profiles
0744     KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
0745     /*KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
0746     if (KdenliveSettings::proxyparams().isEmpty() || KdenliveSettings::proxyextension().isEmpty()) {
0747         KConfigGroup group(&conf, "proxy");
0748         QMap<QString, QString> values = group.entryMap();
0749         QMapIterator<QString, QString> i(values);
0750         if (i.hasNext()) {
0751             i.next();
0752             QString proxystring = i.value();
0753             KdenliveSettings::setProxyparams(proxystring.section(QLatin1Char(';'), 0, 0));
0754             KdenliveSettings::setProxyextension(proxystring.section(QLatin1Char(';'), 1, 1));
0755         }
0756     }*/
0757     if (KdenliveSettings::v4l_parameters().isEmpty() || KdenliveSettings::v4l_extension().isEmpty()) {
0758         KConfigGroup group(&conf, "video4linux");
0759         QMap<QString, QString> values = group.entryMap();
0760         QMapIterator<QString, QString> i(values);
0761         if (i.hasNext()) {
0762             i.next();
0763             QString v4lstring = i.value();
0764             KdenliveSettings::setV4l_parameters(v4lstring.section(QLatin1Char(';'), 0, 0));
0765             KdenliveSettings::setV4l_extension(v4lstring.section(QLatin1Char(';'), 1, 1));
0766         }
0767     }
0768     if (KdenliveSettings::grab_parameters().isEmpty() || KdenliveSettings::grab_extension().isEmpty()) {
0769         KConfigGroup group(&conf, "screengrab");
0770         QMap<QString, QString> values = group.entryMap();
0771         QMapIterator<QString, QString> i(values);
0772         if (i.hasNext()) {
0773             i.next();
0774             QString grabstring = i.value();
0775             KdenliveSettings::setGrab_parameters(grabstring.section(QLatin1Char(';'), 0, 0));
0776             KdenliveSettings::setGrab_extension(grabstring.section(QLatin1Char(';'), 1, 1));
0777         }
0778     }
0779     if (KdenliveSettings::decklink_parameters().isEmpty() || KdenliveSettings::decklink_extension().isEmpty()) {
0780         KConfigGroup group(&conf, "decklink");
0781         QMap<QString, QString> values = group.entryMap();
0782         QMapIterator<QString, QString> i(values);
0783         if (i.hasNext()) {
0784             i.next();
0785             QString decklinkstring = i.value();
0786             KdenliveSettings::setDecklink_parameters(decklinkstring.section(QLatin1Char(';'), 0, 0));
0787             KdenliveSettings::setDecklink_extension(decklinkstring.section(QLatin1Char(';'), 1, 1));
0788         }
0789     }
0790     if (!QDir(KdenliveSettings::currenttmpfolder()).isReadable())
0791         KdenliveSettings::setCurrenttmpfolder(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
0792 
0793     if (firstRun) {
0794         // Load editing layout
0795         layoutManager->loadLayout(QStringLiteral("kdenlive_editing"), true);
0796     }
0797 
0798 #ifdef USE_JOGSHUTTLE
0799     new JogManager(this);
0800 #endif
0801     m_timelineTabs->setTimelineMenu(compositionMenu, timelineMenu, guideMenu, timelineRulerMenu, actionCollection()->action(QStringLiteral("edit_guide")),
0802                                     timelineHeadersMenu, thumbsMenu, timelineSubtitleMenu);
0803     scmanager->slotCheckActiveScopes();
0804     connect(qApp, &QGuiApplication::applicationStateChanged, this, [&](Qt::ApplicationState state) {
0805         if (state == Qt::ApplicationActive && getCurrentTimeline()) {
0806             getCurrentTimeline()->regainFocus();
0807         }
0808     });
0809     connect(this, &MainWindow::removeBinDock, this, &MainWindow::slotRemoveBinDock);
0810     // m_messageLabel->setMessage(QStringLiteral("This is a beta version. Always backup your data"), MltError);
0811 
0812     QAction *const showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar));
0813     // FIXME: workaround for BUG 171080
0814     showMenuBarAction->setChecked(!menuBar()->isHidden());
0815 
0816     m_hamburgerMenu = KStandardAction::hamburgerMenu(nullptr, nullptr, actionCollection());
0817     // after the QMenuBar has been initialised
0818     m_hamburgerMenu->setMenuBar(menuBar());
0819     m_hamburgerMenu->setShowMenuBarAction(showMenuBarAction);
0820 
0821     // Detect shortcut conflicts bewtween mainwindow and media browser
0822     pCore->mediaBrowser()->detectShortcutConflicts();
0823 
0824     connect(toolBar(), &KToolBar::visibilityChanged, this, [&, showMenuBarAction](bool visible) {
0825         if (visible && !toolBar()->actions().contains(m_hamburgerMenu)) {
0826             // hack to be able to insert the hamburger menu at the first position
0827             QAction *const firstChild = toolBar()->actionAt(toolBar()->height() / 2, toolBar()->height() / 2);
0828             QAction *const seperator = toolBar()->insertSeparator(firstChild);
0829             toolBar()->insertAction(seperator, m_hamburgerMenu);
0830             m_hamburgerMenu->hideActionsOf(toolBar());
0831         }
0832     });
0833 }
0834 
0835 void MainWindow::loadContainerActions()
0836 {
0837     // Build all actions using the static_cast<QMenu *>(factory()->container method
0838     // That need to be rebuild when updating the ui.rc file
0839 
0840     // Redirect help entry to our own function
0841     // First delete the default help action
0842     QAction *officialHelp = actionCollection()->action(KStandardAction::name(KStandardAction::HelpContents));
0843     actionCollection()->removeAction(officialHelp);
0844     // Now recreate our own
0845     KStandardAction::helpContents(this, &MainWindow::appHelpActivated, actionCollection());
0846     officialHelp = actionCollection()->action(KStandardAction::name(KStandardAction::HelpContents));
0847     // Replug it in the Help menu
0848     QMenu *helpMenu = static_cast<QMenu *>(factory()->container(QStringLiteral("help"), this));
0849     if (helpMenu) {
0850         QAction *whatsThis = actionCollection()->action(KStandardAction::name(KStandardAction::WhatsThis));
0851         helpMenu->insertAction(whatsThis, officialHelp);
0852     }
0853 
0854     // rebuild timeline clip menu
0855     m_timelineTabs->buildClipMenu();
0856 
0857     // Ensure the "Monitor Config" menu doesn't replace the settings dialog on Mac because of text heuristics
0858     auto *monitorConfig = qobject_cast<QMenu *>(factory()->container(QStringLiteral("monitor_config"), this));
0859     if (monitorConfig) {
0860         monitorConfig->menuAction()->setMenuRole(QAction::NoRole);
0861     }
0862 
0863     // Connect monitor overlay info menu.
0864     QMenu *monitorOverlay = static_cast<QMenu *>(factory()->container(QStringLiteral("monitor_config_overlay"), this));
0865     if (monitorOverlay) {
0866         connect(monitorOverlay, &QMenu::triggered, this, &MainWindow::slotSwitchMonitorOverlay);
0867 
0868         m_projectMonitor->setupMenu(static_cast<QMenu *>(factory()->container(QStringLiteral("monitor_go"), this)), monitorOverlay, m_playZone, m_loopZone,
0869                                     nullptr, m_loopClip);
0870         m_clipMonitor->setupMenu(static_cast<QMenu *>(factory()->container(QStringLiteral("monitor_go"), this)), monitorOverlay, m_playZone, m_loopZone,
0871                                  static_cast<QMenu *>(factory()->container(QStringLiteral("marker_menu"), this)), nullptr);
0872     }
0873 
0874     QMenu *clipInTimeline = static_cast<QMenu *>(factory()->container(QStringLiteral("clip_in_timeline"), this));
0875     clipInTimeline->setIcon(QIcon::fromTheme(QStringLiteral("go-jump")));
0876 
0877     QMenu *m = static_cast<QMenu *>(factory()->container(QStringLiteral("video_effects_menu"), this));
0878     connect(m, &QMenu::triggered, this, &MainWindow::slotAddEffect);
0879 
0880     QMenu *addMenu = static_cast<QMenu *>(factory()->container(QStringLiteral("generators"), this));
0881     Generators::getGenerators(KdenliveSettings::producerslist(), addMenu);
0882     connect(addMenu, &QMenu::triggered, this, &MainWindow::buildGenerator);
0883 }
0884 
0885 void MainWindow::slotThemeChanged(const QString &name)
0886 {
0887     KSharedConfigPtr config = KSharedConfig::openConfig(name);
0888     QPalette plt = KColorScheme::createApplicationPalette(config);
0889     // qApp->setPalette(plt);
0890     // Required for qml palette change
0891     QGuiApplication::setPalette(plt);
0892 
0893     QColor background = plt.window().color();
0894     bool useDarkIcons = background.value() < 100;
0895 
0896     if (m_assetPanel) {
0897         m_assetPanel->updatePalette();
0898     }
0899     if (m_clipMonitor) {
0900         m_clipMonitor->setPalette(plt);
0901     }
0902     if (m_projectMonitor) {
0903         m_projectMonitor->setPalette(plt);
0904     }
0905     if (m_timelineTabs) {
0906         m_timelineTabs->setPalette(plt);
0907         getCurrentTimeline()->controller()->resetView();
0908     }
0909     if (m_audioSpectrum) {
0910         m_audioSpectrum->refreshPixmap();
0911     }
0912     Q_EMIT pCore->updatePalette();
0913 
0914     KSharedConfigPtr kconfig = KSharedConfig::openConfig();
0915     KConfigGroup initialGroup(kconfig, "version");
0916     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
0917     bool isAppimage = pCore->packageType() == QStringLiteral("appimage");
0918     bool isKDE = env.value(QStringLiteral("XDG_CURRENT_DESKTOP")).toLower() == QLatin1String("kde");
0919     bool forceBreeze = initialGroup.exists() && KdenliveSettings::force_breeze();
0920     if ((!isKDE || isAppimage || forceBreeze) &&
0921         ((useDarkIcons && QIcon::themeName() == QStringLiteral("breeze")) || (!useDarkIcons && QIcon::themeName() == QStringLiteral("breeze-dark")))) {
0922         // We need to reload icon theme, on KDE desktops this is not necessary, however for the Appimage it is even on KDE Desktop
0923         // See also https://kate-editor.org/post/2021/2021-03-07-cross-platform-light-dark-themes-and-icons/
0924         QIcon::setThemeName(useDarkIcons ? QStringLiteral("breeze-dark") : QStringLiteral("breeze"));
0925         KdenliveSettings::setUse_dark_breeze(useDarkIcons);
0926     }
0927 }
0928 
0929 MainWindow::~MainWindow()
0930 {
0931     pCore->prepareShutdown();
0932     delete m_timelineTabs;
0933     delete m_audioSpectrum;
0934     if (m_projectMonitor) {
0935         m_projectMonitor->stop();
0936     }
0937     if (m_clipMonitor) {
0938         m_clipMonitor->stop();
0939     }
0940     ClipController::mediaUnavailable.reset();
0941     delete m_projectMonitor;
0942     delete m_clipMonitor;
0943     delete m_shortcutRemoveFocus;
0944     delete m_effectList2;
0945     delete m_compositionList;
0946     pCore->finishShutdown();
0947     qDeleteAll(m_transitions);
0948     Mlt::Factory::close();
0949 }
0950 
0951 // virtual
0952 bool MainWindow::queryClose()
0953 {
0954     if (m_renderWidget) {
0955         int waitingJobs = m_renderWidget->waitingJobsCount();
0956         if (waitingJobs > 0) {
0957             switch (KMessageBox::warningTwoActionsCancel(this,
0958                                                          i18np("You have 1 rendering job waiting in the queue.\nWhat do you want to do with this job?",
0959                                                                "You have %1 rendering jobs waiting in the queue.\nWhat do you want to do with these jobs?",
0960                                                                waitingJobs),
0961                                                          QString(), KGuiItem(i18n("Start them now")), KGuiItem(i18n("Delete them")))) {
0962             case KMessageBox::PrimaryAction:
0963                 // create script with waiting jobs and start it
0964                 if (!m_renderWidget->startWaitingRenderJobs()) {
0965                     return false;
0966                 }
0967                 break;
0968             case KMessageBox::SecondaryAction:
0969                 // Don't do anything, jobs will be deleted
0970                 break;
0971             default:
0972                 return false;
0973             }
0974         }
0975     }
0976     saveOptions();
0977 
0978     // WARNING: According to KMainWindow::queryClose documentation we are not supposed to close the document here?
0979     return pCore->projectManager()->closeCurrentDocument(true, true);
0980 }
0981 
0982 void MainWindow::buildGenerator(QAction *action)
0983 {
0984     Generators gen(action->data().toString(), this);
0985     if (gen.exec() == QDialog::Accepted) {
0986         pCore->bin()->slotAddClipToProject(gen.getSavedClip());
0987     }
0988 }
0989 
0990 void MainWindow::saveProperties(KConfigGroup &config)
0991 {
0992     // save properties here
0993     KXmlGuiWindow::saveProperties(config);
0994     // TODO: fix session management
0995     if (qApp->isSavingSession() && pCore->projectManager()) {
0996         if (pCore->currentDoc() && !pCore->currentDoc()->url().isEmpty()) {
0997             config.writeEntry("kdenlive_lastUrl", pCore->currentDoc()->url().toLocalFile());
0998         }
0999     }
1000 }
1001 
1002 void MainWindow::saveNewToolbarConfig()
1003 {
1004     KXmlGuiWindow::saveNewToolbarConfig();
1005     // TODO for some reason all dynamically inserted actions are removed by the save toolbar
1006     // So we currently re-add them manually....
1007     loadDockActions();
1008     loadClipActions();
1009     loadContainerActions();
1010     for (auto &bin : m_binWidgets) {
1011         bin->setupGeneratorMenu();
1012     }
1013 
1014     // hack to be able to insert the hamburger menu at the first position
1015     QAction *const firstChild = toolBar()->actionAt(toolBar()->height() / 2, toolBar()->height() / 2);
1016     QAction *const seperator = toolBar()->insertSeparator(firstChild);
1017     toolBar()->insertAction(seperator, m_hamburgerMenu);
1018     m_hamburgerMenu->hideActionsOf(toolBar());
1019 }
1020 
1021 void MainWindow::slotReloadEffects(const QStringList &paths)
1022 {
1023     for (const QString &p : paths) {
1024         EffectsRepository::get()->reloadCustom(p);
1025     }
1026     m_effectList2->reloadEffectMenu(m_effectsMenu, m_effectActions);
1027 }
1028 
1029 void MainWindow::configureNotifications()
1030 {
1031     KNotifyConfigWidget::configure(this);
1032 }
1033 
1034 void MainWindow::slotFullScreen()
1035 {
1036     KToggleFullScreenAction::setFullScreen(this, actionCollection()->action(QStringLiteral("fullscreen"))->isChecked());
1037 }
1038 
1039 void MainWindow::slotConnectMonitors()
1040 {
1041     // connect(m_projectList, SIGNAL(deleteProjectClips(QStringList,QMap<QString,QString>)), this,
1042     // SLOT(slotDeleteProjectClips(QStringList,QMap<QString,QString>)));
1043     connect(m_clipMonitor, &Monitor::refreshClipThumbnail, pCore->bin(), &Bin::slotRefreshClipThumbnail);
1044     connect(m_projectMonitor, &Monitor::requestFrameForAnalysis, this, &MainWindow::slotMonitorRequestRenderFrame);
1045     connect(m_projectMonitor, &Monitor::createSplitOverlay, this, &MainWindow::createSplitOverlay, Qt::DirectConnection);
1046     connect(m_projectMonitor, &Monitor::removeSplitOverlay, this, &MainWindow::removeSplitOverlay, Qt::DirectConnection);
1047 }
1048 
1049 void MainWindow::createSplitOverlay(std::shared_ptr<Mlt::Filter> filter)
1050 {
1051     if (m_assetPanel->effectStackOwner().type == KdenliveObjectType::TimelineClip) {
1052         getCurrentTimeline()->controller()->createSplitOverlay(m_assetPanel->effectStackOwner().itemId, filter);
1053         m_projectMonitor->activateSplit();
1054     } else {
1055         pCore->displayMessage(i18n("Select a clip to compare effect"), ErrorMessage);
1056     }
1057 }
1058 
1059 void MainWindow::removeSplitOverlay()
1060 {
1061     getCurrentTimeline()->controller()->removeSplitOverlay();
1062 }
1063 
1064 void MainWindow::addAction(const QString &name, QAction *action, const QKeySequence &shortcut, KActionCategory *category)
1065 {
1066     m_actionNames.append(name);
1067     if (category) {
1068         category->addAction(name, action);
1069     } else {
1070         actionCollection()->addAction(name, action);
1071     }
1072     actionCollection()->setDefaultShortcut(action, shortcut);
1073 }
1074 
1075 void MainWindow::addAction(const QString &name, QAction *action, const QKeySequence &shortcut, const QString &category)
1076 {
1077     addAction(name, action, shortcut, kdenliveCategoryMap.value(category, nullptr));
1078 }
1079 
1080 QAction *MainWindow::addAction(const QString &name, const QString &text, const QObject *receiver, const char *member, const QIcon &icon,
1081                                const QKeySequence &shortcut, KActionCategory *category)
1082 {
1083     auto *action = new QAction(text, this);
1084     if (!icon.isNull()) {
1085         action->setIcon(icon);
1086     }
1087     addAction(name, action, shortcut, category);
1088     connect(action, SIGNAL(triggered(bool)), receiver, member);
1089 
1090     return action;
1091 }
1092 
1093 QAction *MainWindow::addAction(const QString &name, const QString &text, const QObject *receiver, const char *member, const QIcon &icon,
1094                                const QKeySequence &shortcut, const QString &category)
1095 {
1096     return addAction(name, text, receiver, member, icon, shortcut, kdenliveCategoryMap.value(category, nullptr));
1097 }
1098 
1099 void MainWindow::setupActions()
1100 {
1101     // create edit mode buttons
1102     m_normalEditTool = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-normal-edit")), i18n("Normal Mode"), this);
1103     m_normalEditTool->setCheckable(true);
1104     m_normalEditTool->setChecked(true);
1105 
1106     m_overwriteEditTool = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-overwrite-edit")), i18n("Overwrite Mode"), this);
1107     m_overwriteEditTool->setCheckable(true);
1108     m_overwriteEditTool->setChecked(false);
1109 
1110     m_insertEditTool = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-insert-edit")), i18n("Insert Mode"), this);
1111     m_insertEditTool->setCheckable(true);
1112     m_insertEditTool->setChecked(false);
1113 
1114     KSelectAction *sceneMode = new KSelectAction(i18n("Timeline Edit Mode"), this);
1115     sceneMode->setWhatsThis(
1116         xi18nc("@info:whatsthis", "Switches between Normal, Overwrite and Insert Mode. Determines the default action when handling clips in the timeline."));
1117     sceneMode->addAction(m_normalEditTool);
1118     sceneMode->addAction(m_overwriteEditTool);
1119     sceneMode->addAction(m_insertEditTool);
1120     sceneMode->setCurrentItem(0);
1121 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 240, 0)
1122     connect(sceneMode, &KSelectAction::actionTriggered, this, &MainWindow::slotChangeEdit);
1123 #else
1124     connect(sceneMode, static_cast<void (KSelectAction::*)(QAction *)>(&KSelectAction::triggered), this, &MainWindow::slotChangeEdit);
1125 #endif
1126     addAction(QStringLiteral("timeline_mode"), sceneMode);
1127     actionCollection()->setShortcutsConfigurable(sceneMode, false);
1128 
1129     m_useTimelineZone = new KDualAction(i18n("Do not Use Timeline Zone for Insert"), i18n("Use Timeline Zone for Insert"), this);
1130     m_useTimelineZone->setWhatsThis(xi18nc("@info:whatsthis", "Toggles between using the timeline zone for inserting (on) or not (off)."));
1131     m_useTimelineZone->setActiveIcon(QIcon::fromTheme(QStringLiteral("timeline-use-zone-on")));
1132     m_useTimelineZone->setInactiveIcon(QIcon::fromTheme(QStringLiteral("timeline-use-zone-off")));
1133     m_useTimelineZone->setAutoToggle(true);
1134     connect(m_useTimelineZone, &KDualAction::activeChangedByUser, this, &MainWindow::slotSwitchTimelineZone);
1135     addAction(QStringLiteral("use_timeline_zone_in_edit"), m_useTimelineZone);
1136 
1137     m_compositeAction = new QAction(i18n("Enable Track Compositing"), this);
1138     m_compositeAction->setCheckable(true);
1139     connect(m_compositeAction, &QAction::triggered, this, &MainWindow::slotUpdateCompositing);
1140     addAction(QStringLiteral("timeline_compositing"), m_compositeAction);
1141     actionCollection()->setShortcutsConfigurable(m_compositeAction, false);
1142 
1143     QAction *splitView = new QAction(QIcon::fromTheme(QStringLiteral("view-split-top-bottom")), i18n("Split Audio Tracks"), this);
1144     addAction(QStringLiteral("timeline_view_split"), splitView);
1145     splitView->setData(QVariant::fromValue(1));
1146     splitView->setCheckable(true);
1147     splitView->setChecked(KdenliveSettings::audiotracksbelow() == 1);
1148 
1149     QAction *splitView2 = new QAction(QIcon::fromTheme(QStringLiteral("view-split-top-bottom")), i18n("Split Audio Tracks (reverse)"), this);
1150     addAction(QStringLiteral("timeline_view_split_reverse"), splitView2);
1151     splitView2->setData(QVariant::fromValue(2));
1152     splitView2->setCheckable(true);
1153     splitView2->setChecked(KdenliveSettings::audiotracksbelow() == 2);
1154 
1155     QAction *mixedView = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Mixed Audio tracks"), this);
1156     addAction(QStringLiteral("timeline_mixed_view"), mixedView);
1157     mixedView->setData(QVariant::fromValue(0));
1158     mixedView->setCheckable(true);
1159     mixedView->setChecked(KdenliveSettings::audiotracksbelow() == 0);
1160 
1161     auto *clipTypeGroup = new QActionGroup(this);
1162     clipTypeGroup->addAction(mixedView);
1163     clipTypeGroup->addAction(splitView);
1164     clipTypeGroup->addAction(splitView2);
1165     connect(clipTypeGroup, &QActionGroup::triggered, this, &MainWindow::slotUpdateTimelineView);
1166 
1167     auto tlsettings = new QMenu(this);
1168     tlsettings->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
1169     tlsettings->addAction(m_compositeAction);
1170     tlsettings->addSeparator();
1171     tlsettings->addAction(mixedView);
1172     tlsettings->addAction(splitView);
1173     tlsettings->addAction(splitView2);
1174 
1175     auto *timelineSett = new QToolButton(this);
1176     timelineSett->setPopupMode(QToolButton::InstantPopup);
1177     timelineSett->setMenu(tlsettings);
1178     timelineSett->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
1179     auto *tlButtonAction = new QWidgetAction(this);
1180     tlButtonAction->setDefaultWidget(timelineSett);
1181     tlButtonAction->setText(i18n("Track menu"));
1182     addAction(QStringLiteral("timeline_settings"), tlButtonAction);
1183 
1184     m_timeFormatButton = new KSelectAction(QStringLiteral("00:00:00:00 / 00:00:00:00"), this);
1185     m_timeFormatButton->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
1186     m_timeFormatButton->addAction(i18n("hh:mm:ss:ff"));
1187     m_timeFormatButton->addAction(i18n("Frames"));
1188     if (KdenliveSettings::frametimecode()) {
1189         m_timeFormatButton->setCurrentItem(1);
1190     } else {
1191         m_timeFormatButton->setCurrentItem(0);
1192     }
1193     connect(m_timeFormatButton, &KSelectAction::indexTriggered, this, &MainWindow::slotUpdateTimecodeFormat);
1194     m_timeFormatButton->setToolBarMode(KSelectAction::MenuMode);
1195     m_timeFormatButton->setToolButtonPopupMode(QToolButton::InstantPopup);
1196     addAction(QStringLiteral("timeline_timecode"), m_timeFormatButton);
1197     actionCollection()->setShortcutsConfigurable(m_timeFormatButton, false);
1198 
1199     m_buttonSubtitleEditTool = new QAction(QIcon::fromTheme(QStringLiteral("add-subtitle")), i18n("Edit Subtitle Tool"), this);
1200     m_buttonSubtitleEditTool->setWhatsThis(xi18nc("@info:whatsthis", "Toggles the subtitle track in the timeline."));
1201     m_buttonSubtitleEditTool->setCheckable(true);
1202     m_buttonSubtitleEditTool->setChecked(false);
1203     addAction(QStringLiteral("subtitle_tool"), m_buttonSubtitleEditTool);
1204     connect(m_buttonSubtitleEditTool, &QAction::triggered, this, &MainWindow::slotShowSubtitles);
1205 
1206     // create tools buttons
1207     m_buttonSelectTool = new QAction(QIcon::fromTheme(QStringLiteral("cursor-arrow")), i18n("Selection Tool"), this);
1208     // toolbar->addAction(m_buttonSelectTool);
1209     m_buttonSelectTool->setCheckable(true);
1210     m_buttonSelectTool->setChecked(true);
1211 
1212     m_buttonRazorTool = new QAction(QIcon::fromTheme(QStringLiteral("edit-cut")), i18n("Razor Tool"), this);
1213     // toolbar->addAction(m_buttonRazorTool);
1214     m_buttonRazorTool->setCheckable(true);
1215     m_buttonRazorTool->setChecked(false);
1216 
1217     m_buttonSpacerTool = new QAction(QIcon::fromTheme(QStringLiteral("distribute-horizontal-x")), i18n("Spacer Tool"), this);
1218     m_buttonSpacerTool->setWhatsThis(
1219         xi18nc("@info:whatsthis",
1220                "When selected, clicking and dragging the mouse in the timeline temporarily groups separate clips and creates or removes space between clips."));
1221     // toolbar->addAction(m_buttonSpacerTool);
1222     m_buttonSpacerTool->setCheckable(true);
1223     m_buttonSpacerTool->setChecked(false);
1224 
1225     m_buttonRippleTool = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-ripple")), i18n("Ripple Tool"), this);
1226     m_buttonRippleTool->setWhatsThis(
1227         xi18nc("@info:whatsthis",
1228                "When selected, dragging the edges of a clip lengthens or shortens the clip and moves adjacent clips back and forth while doing that."));
1229     m_buttonRippleTool->setCheckable(true);
1230     m_buttonRippleTool->setChecked(false);
1231 
1232     /* TODO Implement Roll
1233     // TODO icon available (and properly working) in KF 5.86
1234     m_buttonRollTool = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-rolling")), i18n("Roll Tool"), this);
1235 
1236     m_buttonRollTool->setCheckable(true);
1237     m_buttonRollTool->setChecked(false);*/
1238 
1239     m_buttonSlipTool = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-slip")), i18n("Slip Tool"), this);
1240     m_buttonSlipTool->setWhatsThis(xi18nc("@info:whatsthis", "When selected, dragging a clip slips the clip beneath the given window back and forth."));
1241     m_buttonSlipTool->setCheckable(true);
1242     m_buttonSlipTool->setChecked(false);
1243 
1244     m_buttonMulticamTool = new QAction(QIcon::fromTheme(QStringLiteral("view-split-left-right")), i18n("Multicam Tool"), this);
1245     m_buttonMulticamTool->setCheckable(true);
1246     m_buttonMulticamTool->setChecked(false);
1247 
1248     /* TODO Implement Slide
1249     // TODO icon available (and properly working) in KF 5.86
1250     m_buttonSlideTool = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-slide")), i18n("Slide Tool"), this);
1251     m_buttonSlideTool->setCheckable(true);
1252     m_buttonSlideTool->setChecked(false);*/
1253 
1254     auto *toolGroup = new QActionGroup(this);
1255     toolGroup->addAction(m_buttonSelectTool);
1256     toolGroup->addAction(m_buttonRazorTool);
1257     toolGroup->addAction(m_buttonSpacerTool);
1258     toolGroup->addAction(m_buttonRippleTool);
1259     // toolGroup->addAction(m_buttonRollTool);
1260     toolGroup->addAction(m_buttonSlipTool);
1261     // toolGroup->addAction(m_buttonSlideTool);
1262     toolGroup->addAction(m_buttonMulticamTool);
1263 
1264     toolGroup->setExclusive(true);
1265 
1266     QAction *collapseItem = new QAction(QIcon::fromTheme(QStringLiteral("collapse-all")), i18n("Collapse/Expand Item"), this);
1267     addAction(QStringLiteral("collapse_expand"), collapseItem, Qt::Key_Less);
1268     connect(collapseItem, &QAction::triggered, this, &MainWindow::slotCollapse);
1269 
1270     QAction *sameTrack = new QAction(QIcon::fromTheme(QStringLiteral("composite-track-preview")), i18n("Mix Clips"), this);
1271     sameTrack->setWhatsThis(
1272         xi18nc("@info:whatsthis", "Creates a same-track transition between the selected clip and the adjacent one closest to the playhead."));
1273     addAction(QStringLiteral("mix_clip"), sameTrack, Qt::Key_U);
1274     connect(sameTrack, &QAction::triggered, this, [this]() { getCurrentTimeline()->controller()->mixClip(); });
1275 
1276     // toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
1277 
1278     connect(toolGroup, &QActionGroup::triggered, this, &MainWindow::slotChangeTool);
1279 
1280     m_buttonVideoThumbs = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-show-videothumb")), i18n("Show Video Thumbnails"), this);
1281     m_buttonVideoThumbs->setWhatsThis(xi18nc("@info:whatsthis", "Toggles the display of video thumbnails for the clips in the timeline (default is On)."));
1282 
1283     m_buttonVideoThumbs->setCheckable(true);
1284     m_buttonVideoThumbs->setChecked(KdenliveSettings::videothumbnails());
1285     connect(m_buttonVideoThumbs, &QAction::triggered, this, &MainWindow::slotSwitchVideoThumbs);
1286 
1287     m_buttonAudioThumbs = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-show-audiothumb")), i18n("Show Audio Thumbnails"), this);
1288     m_buttonAudioThumbs->setWhatsThis(xi18nc("@info:whatsthis", "Toggles the display of audio thumbnails for the clips in the timeline (default is On)."));
1289 
1290     m_buttonAudioThumbs->setCheckable(true);
1291     m_buttonAudioThumbs->setChecked(KdenliveSettings::audiothumbnails());
1292     connect(m_buttonAudioThumbs, &QAction::triggered, this, &MainWindow::slotSwitchAudioThumbs);
1293 
1294     m_buttonShowMarkers = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-show-markers")), i18n("Show Markers Comments"), this);
1295 
1296     m_buttonShowMarkers->setCheckable(true);
1297     m_buttonShowMarkers->setChecked(KdenliveSettings::showmarkers());
1298     connect(m_buttonShowMarkers, &QAction::triggered, this, &MainWindow::slotSwitchMarkersComments);
1299 
1300     m_buttonSnap = new QAction(QIcon::fromTheme(QStringLiteral("snap")), i18n("Snap"), this);
1301     m_buttonSnap->setWhatsThis(xi18nc("@info:whatsthis", "Toggles the snap function (clips snap to playhead, edges, markers, guides and others)."));
1302 
1303     m_buttonSnap->setCheckable(true);
1304     m_buttonSnap->setChecked(KdenliveSettings::snaptopoints());
1305     connect(m_buttonSnap, &QAction::triggered, this, &MainWindow::slotSwitchSnap);
1306 
1307     m_buttonTimelineTags = new QAction(QIcon::fromTheme(QStringLiteral("tag")), i18n("Show Color Tags in Timeline"), this);
1308     m_buttonTimelineTags->setWhatsThis(xi18nc("@info:whatsthis", "Toggles the display of clip tags in the timeline (default is On)."));
1309 
1310     m_buttonTimelineTags->setCheckable(true);
1311     m_buttonTimelineTags->setChecked(KdenliveSettings::tagsintimeline());
1312     connect(m_buttonTimelineTags, &QAction::triggered, this, &MainWindow::slotShowTimelineTags);
1313 
1314     m_buttonFitZoom = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-best")), i18n("Fit Zoom to Project"), this);
1315     m_buttonFitZoom->setWhatsThis(xi18nc("@info:whatsthis", "Adjusts the zoom level to fit the entire project into the timeline windows."));
1316 
1317     m_buttonFitZoom->setCheckable(false);
1318 
1319     m_zoomSlider = new QSlider(Qt::Horizontal, this);
1320     m_zoomSlider->setRange(0, 20);
1321     m_zoomSlider->setPageStep(1);
1322     m_zoomSlider->setInvertedAppearance(true);
1323     m_zoomSlider->setInvertedControls(true);
1324 
1325     m_zoomSlider->setMaximumWidth(150);
1326     m_zoomSlider->setMinimumWidth(100);
1327 
1328     m_zoomIn = KStandardAction::zoomIn(this, SLOT(slotZoomIn()), actionCollection());
1329     m_zoomOut = KStandardAction::zoomOut(this, SLOT(slotZoomOut()), actionCollection());
1330 
1331     connect(m_zoomSlider, &QSlider::valueChanged, this, [&](int value) { slotSetZoom(value); });
1332     connect(m_zoomSlider, &QAbstractSlider::sliderMoved, this, &MainWindow::slotShowZoomSliderToolTip);
1333     connect(m_buttonFitZoom, &QAction::triggered, this, &MainWindow::slotFitZoom);
1334 
1335     KToolBar *toolbar = new KToolBar(QStringLiteral("statusToolBar"), this, Qt::BottomToolBarArea);
1336     toolbar->setMovable(false);
1337     toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
1338 
1339     if (KdenliveSettings::gpu_accel()) {
1340         QLabel *warnLabel = new QLabel(i18n("Experimental GPU processing enabled - not for production"), this);
1341         warnLabel->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
1342         warnLabel->setAlignment(Qt::AlignHCenter);
1343         warnLabel->setStyleSheet(QStringLiteral("QLabel { background-color :red; color:black;padding-left:2px;padding-right:2px}"));
1344         toolbar->addWidget(warnLabel);
1345     }
1346 
1347     m_trimLabel = new QLabel(QString(), this);
1348     m_trimLabel->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
1349     m_trimLabel->setAlignment(Qt::AlignHCenter);
1350     m_trimLabel->setMinimumWidth(m_trimLabel->fontMetrics().boundingRect(i18n("Multicam")).width() + 8);
1351     m_trimLabel->setToolTip(i18n("Active tool and editing mode"));
1352 
1353     toolbar->addWidget(m_trimLabel);
1354     toolbar->addSeparator();
1355     toolbar->addAction(m_buttonTimelineTags);
1356     toolbar->addAction(m_buttonVideoThumbs);
1357     toolbar->addAction(m_buttonAudioThumbs);
1358     toolbar->addAction(m_buttonShowMarkers);
1359     toolbar->addAction(m_buttonSnap);
1360     toolbar->addSeparator();
1361     toolbar->addAction(m_buttonFitZoom);
1362     toolbar->addAction(m_zoomOut);
1363     toolbar->addWidget(m_zoomSlider);
1364     toolbar->addAction(m_zoomIn);
1365 
1366     int small = style()->pixelMetric(QStyle::PM_SmallIconSize);
1367     statusBar()->setMaximumHeight(2 * small);
1368     m_messageLabel = new StatusBarMessageLabel(this);
1369     m_messageLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
1370     connect(this, &MainWindow::displayMessage, m_messageLabel, &StatusBarMessageLabel::setMessage);
1371     connect(this, &MainWindow::displaySelectionMessage, m_messageLabel, &StatusBarMessageLabel::setSelectionMessage);
1372     connect(this, &MainWindow::displayProgressMessage, m_messageLabel, &StatusBarMessageLabel::setProgressMessage);
1373     statusBar()->addWidget(m_messageLabel, 10);
1374     statusBar()->addPermanentWidget(toolbar);
1375     toolbar->setIconSize(QSize(small, small));
1376     toolbar->layout()->setContentsMargins(0, 0, 0, 0);
1377     statusBar()->setContentsMargins(0, 0, 0, 0);
1378 
1379     addAction(QStringLiteral("normal_mode"), m_normalEditTool);
1380     addAction(QStringLiteral("overwrite_mode"), m_overwriteEditTool);
1381     addAction(QStringLiteral("insert_mode"), m_insertEditTool);
1382 
1383     KActionCategory *toolsActionCategory = new KActionCategory(i18n("Tools"), actionCollection());
1384     addAction(QStringLiteral("select_tool"), m_buttonSelectTool, Qt::Key_S, toolsActionCategory);
1385     addAction(QStringLiteral("razor_tool"), m_buttonRazorTool, Qt::Key_X, toolsActionCategory);
1386     addAction(QStringLiteral("spacer_tool"), m_buttonSpacerTool, Qt::Key_M, toolsActionCategory);
1387     addAction(QStringLiteral("ripple_tool"), m_buttonRippleTool, {}, toolsActionCategory);
1388     // addAction(QStringLiteral("roll_tool"), m_buttonRollTool, QKeySequence(), toolsActionCategory);
1389     addAction(QStringLiteral("slip_tool"), m_buttonSlipTool, {}, toolsActionCategory);
1390     addAction(QStringLiteral("multicam_tool"), m_buttonMulticamTool, {}, toolsActionCategory);
1391     // addAction(QStringLiteral("slide_tool"), m_buttonSlideTool);
1392 
1393     addAction(QStringLiteral("automatic_transition"), m_buttonTimelineTags);
1394     addAction(QStringLiteral("show_video_thumbs"), m_buttonVideoThumbs);
1395     addAction(QStringLiteral("show_audio_thumbs"), m_buttonAudioThumbs);
1396     addAction(QStringLiteral("show_markers"), m_buttonShowMarkers);
1397     addAction(QStringLiteral("snap"), m_buttonSnap);
1398     addAction(QStringLiteral("zoom_fit"), m_buttonFitZoom);
1399 
1400 #if defined(Q_OS_WIN)
1401     int glBackend = KdenliveSettings::opengl_backend();
1402     QAction *openGLAuto = new QAction(i18n("Auto"), this);
1403     openGLAuto->setData(0);
1404     openGLAuto->setCheckable(true);
1405     openGLAuto->setChecked(glBackend == 0);
1406 
1407     QAction *openGLDesktop = new QAction(i18n("OpenGL"), this);
1408     openGLDesktop->setData(Qt::AA_UseDesktopOpenGL);
1409     openGLDesktop->setCheckable(true);
1410     openGLDesktop->setChecked(glBackend == Qt::AA_UseDesktopOpenGL);
1411 
1412     QAction *openGLES = new QAction(i18n("DirectX (ANGLE)"), this);
1413     openGLES->setData(Qt::AA_UseOpenGLES);
1414     openGLES->setCheckable(true);
1415     openGLES->setChecked(glBackend == Qt::AA_UseOpenGLES);
1416 
1417     QAction *openGLSoftware = new QAction(i18n("Software OpenGL"), this);
1418     openGLSoftware->setData(Qt::AA_UseSoftwareOpenGL);
1419     openGLSoftware->setCheckable(true);
1420     openGLSoftware->setChecked(glBackend == Qt::AA_UseSoftwareOpenGL);
1421     addAction(QStringLiteral("opengl_auto"), openGLAuto);
1422     addAction(QStringLiteral("opengl_desktop"), openGLDesktop);
1423     addAction(QStringLiteral("opengl_es"), openGLES);
1424     addAction(QStringLiteral("opengl_software"), openGLSoftware);
1425 #endif
1426 
1427     addAction(QStringLiteral("run_wizard"), i18n("Run Config Wizard…"), this, SLOT(slotRunWizard()), QIcon::fromTheme(QStringLiteral("tools-wizard")));
1428     addAction(QStringLiteral("project_settings"), i18n("Project Settings…"), this, SLOT(slotEditProjectSettings()),
1429               QIcon::fromTheme(QStringLiteral("configure")));
1430 
1431     addAction(QStringLiteral("project_render"), i18n("Render…"), this, SLOT(slotRenderProject()), QIcon::fromTheme(QStringLiteral("media-record")),
1432               Qt::CTRL | Qt::Key_Return);
1433 
1434     addAction(QStringLiteral("stop_project_render"), i18n("Stop Render"), this, SLOT(slotStopRenderProject()),
1435               QIcon::fromTheme(QStringLiteral("media-record")));
1436 
1437     addAction(QStringLiteral("project_clean"), i18n("Clean Project"), this, SLOT(slotCleanProject()), QIcon::fromTheme(QStringLiteral("edit-clear")));
1438 
1439     QAction *resetAction = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Reset Configuration…"), this);
1440     addAction(QStringLiteral("reset_config"), resetAction);
1441     connect(resetAction, &QAction::triggered, this, [&]() { slotRestart(true); });
1442 
1443     m_playZone = addAction(QStringLiteral("monitor_play_zone"), i18n("Play Zone"), pCore->monitorManager(), SLOT(slotPlayZone()),
1444                            QIcon::fromTheme(QStringLiteral("media-playback-start")), Qt::CTRL | Qt::Key_Space, QStringLiteral("navandplayback"));
1445     m_loopZone = addAction(QStringLiteral("monitor_loop_zone"), i18n("Loop Zone"), pCore->monitorManager(), SLOT(slotLoopZone()),
1446                            QIcon::fromTheme(QStringLiteral("media-playback-start")), Qt::CTRL | Qt::SHIFT | Qt::Key_Space, QStringLiteral("navandplayback"));
1447     m_loopClip = new QAction(QIcon::fromTheme(QStringLiteral("media-playback-start")), i18n("Loop Selected Clip"), this);
1448     addAction(QStringLiteral("monitor_loop_clip"), m_loopClip);
1449     m_loopClip->setEnabled(false);
1450 
1451     addAction(QStringLiteral("transcode_clip"), i18n("Transcode Clips…"), this, SLOT(slotTranscodeClip()), QIcon::fromTheme(QStringLiteral("edit-copy")));
1452     QAction *exportAction = new QAction(QIcon::fromTheme(QStringLiteral("document-export")), i18n("OpenTimelineIO E&xport…"), this);
1453     connect(exportAction, &QAction::triggered, &m_otioConvertions, &OtioConvertions::slotExportProject);
1454     addAction(QStringLiteral("export_project"), exportAction);
1455     QAction *importAction = new QAction(QIcon::fromTheme(QStringLiteral("document-import")), i18n("OpenTimelineIO &Import…"), this);
1456     connect(importAction, &QAction::triggered, &m_otioConvertions, &OtioConvertions::slotImportProject);
1457     addAction(QStringLiteral("import_project"), importAction);
1458 
1459     addAction(QStringLiteral("archive_project"), i18n("Archive Project…"), this, SLOT(slotArchiveProject()),
1460               QIcon::fromTheme(QStringLiteral("document-save-all")));
1461     addAction(QStringLiteral("switch_monitor"), i18n("Switch Monitor"), this, SLOT(slotSwitchMonitors()), QIcon(), Qt::Key_T);
1462     addAction(QStringLiteral("focus_timecode"), i18n("Focus Timecode"), this, SLOT(slotFocusTimecode()), QIcon(), Qt::Key_Equal);
1463     addAction(QStringLiteral("expand_timeline_clip"), i18n("Expand Clip"), this, SLOT(slotExpandClip()), QIcon::fromTheme(QStringLiteral("document-open")));
1464 
1465     QAction *overlayInfo = new QAction(QIcon::fromTheme(QStringLiteral("help-hint")), i18n("Monitor Info Overlay"), this);
1466     addAction(QStringLiteral("monitor_overlay"), overlayInfo, {}, QStringLiteral("monitor"));
1467     overlayInfo->setCheckable(true);
1468     overlayInfo->setData(0x01);
1469 
1470     QAction *overlayTCInfo = new QAction(QIcon::fromTheme(QStringLiteral("help-hint")), i18n("Monitor Overlay Timecode"), this);
1471     addAction(QStringLiteral("monitor_overlay_tc"), overlayTCInfo, {}, QStringLiteral("monitor"));
1472     overlayTCInfo->setCheckable(true);
1473     overlayTCInfo->setData(0x02);
1474 
1475     QAction *overlayFpsInfo = new QAction(QIcon::fromTheme(QStringLiteral("help-hint")), i18n("Monitor Overlay Playback Fps"), this);
1476     addAction(QStringLiteral("monitor_overlay_fps"), overlayFpsInfo, {}, QStringLiteral("monitor"));
1477     overlayFpsInfo->setCheckable(true);
1478     overlayFpsInfo->setData(0x20);
1479 
1480     QAction *overlayMarkerInfo = new QAction(QIcon::fromTheme(QStringLiteral("help-hint")), i18n("Monitor Overlay Markers"), this);
1481     addAction(QStringLiteral("monitor_overlay_markers"), overlayMarkerInfo, {}, QStringLiteral("monitor"));
1482     overlayMarkerInfo->setCheckable(true);
1483     overlayMarkerInfo->setData(0x04);
1484 
1485     QAction *overlayAudioInfo = new QAction(QIcon::fromTheme(QStringLiteral("help-hint")), i18n("Monitor Overlay Audio Waveform"), this);
1486     addAction(QStringLiteral("monitor_overlay_audiothumb"), overlayAudioInfo, {}, QStringLiteral("monitor"));
1487     overlayAudioInfo->setCheckable(true);
1488     overlayAudioInfo->setData(0x10);
1489 
1490     QAction *overlayClipJobs = new QAction(QIcon::fromTheme(QStringLiteral("help-hint")), i18n("Monitor Overlay Clip Jobs"), this);
1491     addAction(QStringLiteral("monitor_overlay_clipjobs"), overlayClipJobs, {}, QStringLiteral("monitor"));
1492     overlayClipJobs->setCheckable(true);
1493     overlayClipJobs->setData(0x40);
1494 
1495     connect(overlayInfo, &QAction::toggled, this, [&, overlayTCInfo, overlayFpsInfo, overlayMarkerInfo, overlayAudioInfo, overlayClipJobs](bool toggled) {
1496         overlayTCInfo->setEnabled(toggled);
1497         overlayFpsInfo->setEnabled(toggled);
1498         overlayMarkerInfo->setEnabled(toggled);
1499         overlayAudioInfo->setEnabled(toggled);
1500         overlayClipJobs->setEnabled(toggled);
1501     });
1502 
1503     // Monitor resolution scaling
1504     KActionCategory *resolutionActionCategory = new KActionCategory(i18n("Preview Resolution"), actionCollection());
1505     m_scaleGroup = new QActionGroup(this);
1506     m_scaleGroup->setExclusive(true);
1507     m_scaleGroup->setEnabled(!KdenliveSettings::external_display());
1508     QAction *scale_no = new QAction(i18n("Full Resolution (1:1)"), m_scaleGroup);
1509     addAction(QStringLiteral("scale_no_preview"), scale_no, QKeySequence(), resolutionActionCategory);
1510     scale_no->setCheckable(true);
1511     scale_no->setData(1);
1512     QAction *scale_2 = new QAction(i18n("720p"), m_scaleGroup);
1513     addAction(QStringLiteral("scale_2_preview"), scale_2, QKeySequence(), resolutionActionCategory);
1514     scale_2->setCheckable(true);
1515     scale_2->setData(2);
1516     QAction *scale_4 = new QAction(i18n("540p"), m_scaleGroup);
1517     addAction(QStringLiteral("scale_4_preview"), scale_4, QKeySequence(), resolutionActionCategory);
1518     scale_4->setCheckable(true);
1519     scale_4->setData(4);
1520     QAction *scale_8 = new QAction(i18n("360p"), m_scaleGroup);
1521     addAction(QStringLiteral("scale_8_preview"), scale_8, QKeySequence(), resolutionActionCategory);
1522     scale_8->setCheckable(true);
1523     scale_8->setData(8);
1524     QAction *scale_16 = new QAction(i18n("270p"), m_scaleGroup);
1525     addAction(QStringLiteral("scale_16_preview"), scale_16, QKeySequence(), resolutionActionCategory);
1526     scale_16->setCheckable(true);
1527     scale_16->setData(16);
1528     connect(pCore->monitorManager(), &MonitorManager::scalingChanged, this, [scale_2, scale_4, scale_8, scale_16, scale_no]() {
1529         switch (KdenliveSettings::previewScaling()) {
1530         case 2:
1531             scale_2->setChecked(true);
1532             break;
1533         case 4:
1534             scale_4->setChecked(true);
1535             break;
1536         case 8:
1537             scale_8->setChecked(true);
1538             break;
1539         case 16:
1540             scale_16->setChecked(true);
1541             break;
1542         default:
1543             scale_no->setChecked(true);
1544             break;
1545         }
1546     });
1547     Q_EMIT pCore->monitorManager()->scalingChanged();
1548     connect(m_scaleGroup, &QActionGroup::triggered, this, [](QAction *ac) {
1549         int scaling = ac->data().toInt();
1550         KdenliveSettings::setPreviewScaling(scaling);
1551         // Clear timeline selection so that any qml monitor scene is reset
1552         Q_EMIT pCore->monitorManager()->updatePreviewScaling();
1553     });
1554 
1555     QAction *dropFrames = new QAction(QIcon(), i18n("Real Time (drop frames)"), this);
1556     dropFrames->setCheckable(true);
1557     dropFrames->setChecked(KdenliveSettings::monitor_dropframes());
1558     addAction(QStringLiteral("mlt_realtime"), dropFrames);
1559     connect(dropFrames, &QAction::toggled, this, &MainWindow::slotSwitchDropFrames);
1560 
1561     KSelectAction *monitorGamma = new KSelectAction(i18n("Monitor Gamma"), this);
1562     monitorGamma->addAction(i18n("sRGB (computer)"));
1563     monitorGamma->addAction(i18n("Rec. 709 (TV)"));
1564     addAction(QStringLiteral("mlt_gamma"), monitorGamma, {}, QStringLiteral("monitor"));
1565     monitorGamma->setCurrentItem(KdenliveSettings::monitor_gamma());
1566     connect(monitorGamma, &KSelectAction::indexTriggered, this, &MainWindow::slotSetMonitorGamma);
1567     actionCollection()->setShortcutsConfigurable(monitorGamma, false);
1568 
1569     QAction *insertBinZone = addAction(QStringLiteral("insert_project_tree"), i18n("Insert Zone in Project Bin"), this, SLOT(slotInsertZoneToTree()),
1570                                        QIcon::fromTheme(QStringLiteral("kdenlive-add-clip")), Qt::CTRL | Qt::Key_I);
1571     insertBinZone->setWhatsThis(xi18nc("@info:whatsthis", "Creates a new clip in the project bin from the defined zone."));
1572 
1573     // TODO: Make these 2 shortcuts context aware to avoid conflict with media browser
1574     addAction(QStringLiteral("monitor_seek_snap_backward"), i18n("Go to Previous Snap Point"), this, SLOT(slotSnapRewind()),
1575               QIcon::fromTheme(QStringLiteral("media-seek-backward")), Qt::ALT | Qt::Key_Left, QStringLiteral("navandplayback"));
1576     addAction(QStringLiteral("monitor_seek_snap_forward"), i18n("Go to Next Snap Point"), this, SLOT(slotSnapForward()),
1577               QIcon::fromTheme(QStringLiteral("media-seek-forward")), Qt::ALT | Qt::Key_Right, QStringLiteral("navandplayback"));
1578 
1579     addAction(QStringLiteral("seek_clip_start"), i18n("Go to Clip Start"), this, SLOT(slotClipStart()), QIcon::fromTheme(QStringLiteral("media-seek-backward")),
1580               Qt::Key_Home, QStringLiteral("navandplayback"));
1581     addAction(QStringLiteral("seek_clip_end"), i18n("Go to Clip End"), this, SLOT(slotClipEnd()), QIcon::fromTheme(QStringLiteral("media-seek-forward")),
1582               Qt::Key_End, QStringLiteral("navandplayback"));
1583     addAction(QStringLiteral("monitor_seek_guide_backward"), i18n("Go to Previous Guide"), this, SLOT(slotGuideRewind()),
1584               QIcon::fromTheme(QStringLiteral("media-seek-backward")), Qt::CTRL | Qt::Key_Left, QStringLiteral("navandplayback"));
1585     addAction(QStringLiteral("monitor_seek_guide_forward"), i18n("Go to Next Guide"), this, SLOT(slotGuideForward()),
1586               QIcon::fromTheme(QStringLiteral("media-seek-forward")), Qt::CTRL | Qt::Key_Right, QStringLiteral("navandplayback"));
1587     addAction(QStringLiteral("align_playhead"), i18n("Align Playhead to Mouse Position"), this, SLOT(slotAlignPlayheadToMousePos()), QIcon(), Qt::Key_P,
1588               QStringLiteral("navandplayback"));
1589 
1590     addAction(QStringLiteral("grab_item"), i18n("Grab Current Item"), this, SLOT(slotGrabItem()), QIcon::fromTheme(QStringLiteral("transform-move")),
1591               Qt::SHIFT | Qt::Key_G);
1592 
1593     QAction *stickTransition = new QAction(i18n("Automatic Transition"), this);
1594     stickTransition->setData(QStringLiteral("auto"));
1595     stickTransition->setCheckable(true);
1596     stickTransition->setEnabled(false);
1597     addAction(QStringLiteral("auto_transition"), stickTransition);
1598     connect(stickTransition, &QAction::triggered, this, &MainWindow::slotAutoTransition);
1599 
1600     QAction *overwriteZone = addAction(QStringLiteral("overwrite_to_in_point"), i18n("Overwrite Clip Zone in Timeline"), this, SLOT(slotInsertClipOverwrite()),
1601                                        QIcon::fromTheme(QStringLiteral("timeline-overwrite")), Qt::Key_B);
1602     overwriteZone->setWhatsThis(xi18nc("@info:whatsthis", "When clicked the zone of the clip currently selected in the project bin is inserted at the playhead "
1603                                                           "position in the active timeline. Clips at the insert position are cut and overwritten."));
1604     QAction *insertZone = addAction(QStringLiteral("insert_to_in_point"), i18n("Insert Clip Zone in Timeline"), this, SLOT(slotInsertClipInsert()),
1605                                     QIcon::fromTheme(QStringLiteral("timeline-insert")), Qt::Key_V);
1606     insertZone->setWhatsThis(xi18nc("@info:whatsthis", "When clicked the zone of the clip currently selected in the project bin is inserted at the playhead "
1607                                                        "position in the active timeline. Clips at the insert position are cut and shifted to the right."));
1608     QAction *extractZone = addAction(QStringLiteral("remove_extract"), i18n("Extract Timeline Zone"), this, SLOT(slotExtractZone()),
1609                                      QIcon::fromTheme(QStringLiteral("timeline-extract")), Qt::SHIFT | Qt::Key_X);
1610     extractZone->setWhatsThis(xi18nc("@info:whatsthis", "Click to delete the timeline zone from the timeline. All clips to the right are shifted left."));
1611     QAction *liftZone = addAction(QStringLiteral("remove_lift"), i18n("Lift Timeline Zone"), this, SLOT(slotLiftZone()),
1612                                   QIcon::fromTheme(QStringLiteral("timeline-lift")), Qt::Key_Z);
1613     liftZone->setWhatsThis(xi18nc("@info:whatsthis", "Click to delete the timeline zone from the timeline. All clips to the right stay in position."));
1614     QAction *addPreviewZone = addAction(QStringLiteral("set_render_timeline_zone"), i18n("Add Preview Zone"), this, SLOT(slotDefinePreviewRender()),
1615                                         QIcon::fromTheme(QStringLiteral("preview-add-zone")));
1616     addPreviewZone->setWhatsThis(xi18nc("@info:whatsthis", "Add the currently defined timeline/selection zone as a preview render zone"));
1617     QAction *removePreviewZone = addAction(QStringLiteral("unset_render_timeline_zone"), i18n("Remove Preview Zone"), this, SLOT(slotRemovePreviewRender()),
1618                                            QIcon::fromTheme(QStringLiteral("preview-remove-zone")));
1619     removePreviewZone->setWhatsThis(xi18nc(
1620         "@info:whatsthis",
1621         "Removes the currently defined timeline/selection zone from the preview render zone. Note that this can leave gaps in the preview render zones."));
1622     QAction *removeAllPreviewZone = addAction(QStringLiteral("clear_render_timeline_zone"), i18n("Remove All Preview Zones"), this,
1623                                               SLOT(slotClearPreviewRender()), QIcon::fromTheme(QStringLiteral("preview-remove-all")));
1624     removeAllPreviewZone->setWhatsThis(xi18nc("@info:whatsthis", "Remove all preview render zones."));
1625     QAction *startPreviewRender = addAction(QStringLiteral("prerender_timeline_zone"), i18n("Start Preview Render"), this, SLOT(slotPreviewRender()),
1626                                             QIcon::fromTheme(QStringLiteral("preview-render-on")), QKeySequence(Qt::SHIFT | Qt::Key_Return));
1627     startPreviewRender->setWhatsThis(xi18nc("@info:whatsthis",
1628                                             "Click to start the rendering of all preview zones (recommended for areas with complex and many effects).<nl/>"
1629                                             "Click on the down-arrow icon to get a list of options (for example: add preview render zone, remove all zones)."));
1630     addAction(QStringLiteral("stop_prerender_timeline"), i18n("Stop Preview Render"), this, SLOT(slotStopPreviewRender()),
1631               QIcon::fromTheme(QStringLiteral("preview-render-off")));
1632 
1633     addAction(QStringLiteral("select_timeline_zone"), i18n("Adjust Timeline Zone to Selection"), this, SLOT(slotSelectTimelineZone()),
1634               QIcon::fromTheme(QStringLiteral("edit-select")), Qt::SHIFT | Qt::Key_Z);
1635     addAction(QStringLiteral("select_timeline_clip"), i18n("Select Clip"), this, SLOT(slotSelectTimelineClip()),
1636               QIcon::fromTheme(QStringLiteral("edit-select")), Qt::Key_Plus);
1637     addAction(QStringLiteral("deselect_timeline_clip"), i18n("Deselect Clip"), this, SLOT(slotDeselectTimelineClip()),
1638               QIcon::fromTheme(QStringLiteral("edit-select")), Qt::Key_Minus);
1639     addAction(QStringLiteral("select_add_timeline_clip"), i18n("Add Clip to Selection"), this, SLOT(slotSelectAddTimelineClip()),
1640               QIcon::fromTheme(QStringLiteral("edit-select")), Qt::ALT | Qt::Key_Plus);
1641     addAction(QStringLiteral("select_timeline_transition"), i18n("Select Transition"), this, SLOT(slotSelectTimelineTransition()),
1642               QIcon::fromTheme(QStringLiteral("edit-select")), Qt::SHIFT | Qt::Key_Plus);
1643     addAction(QStringLiteral("deselect_timeline_transition"), i18n("Deselect Transition"), this, SLOT(slotDeselectTimelineTransition()),
1644               QIcon::fromTheme(QStringLiteral("edit-select")), Qt::SHIFT | Qt::Key_Minus);
1645     addAction(QStringLiteral("select_add_timeline_transition"), i18n("Add Transition to Selection"), this, SLOT(slotSelectAddTimelineTransition()),
1646               QIcon::fromTheme(QStringLiteral("edit-select")), Qt::ALT | Qt::SHIFT | Qt::Key_Plus);
1647 
1648     addAction(QStringLiteral("delete_all_clip_markers"), i18n("Delete All Markers"), this, SLOT(slotDeleteAllClipMarkers()),
1649               QIcon::fromTheme(QStringLiteral("edit-delete")));
1650     addAction(QStringLiteral("add_marker_guide_quickly"), i18n("Add Marker/Guide quickly"), this, SLOT(slotAddMarkerGuideQuickly()),
1651               QIcon::fromTheme(QStringLiteral("bookmark-new")), QKeySequence(Qt::KeypadModifier | Qt::Key_Asterisk));
1652 
1653     // Clip actions. We set some category info on the action data to enable/disable it contextually in timelinecontroller
1654     KActionCategory *clipActionCategory = new KActionCategory(i18n("Current Selection"), actionCollection());
1655 
1656     QAction *addMarker = addAction(QStringLiteral("add_clip_marker"), i18n("Add Marker"), this, SLOT(slotAddClipMarker()),
1657                                    QIcon::fromTheme(QStringLiteral("bookmark-new")), QKeySequence(), clipActionCategory);
1658     addMarker->setData('P');
1659 
1660     QAction *delMarker = addAction(QStringLiteral("delete_clip_marker"), i18n("Delete Marker"), this, SLOT(slotDeleteClipMarker()),
1661                                    QIcon::fromTheme(QStringLiteral("edit-delete")), QKeySequence(), clipActionCategory);
1662     delMarker->setData('P');
1663 
1664     QAction *editClipMarker = addAction(QStringLiteral("edit_clip_marker"), i18n("Edit Marker…"), this, SLOT(slotEditClipMarker()),
1665                                         QIcon::fromTheme(QStringLiteral("document-properties")), QKeySequence(), clipActionCategory);
1666     editClipMarker->setObjectName(QStringLiteral("edit_marker"));
1667     editClipMarker->setData('P');
1668 
1669     QAction *splitAudio = addAction(QStringLiteral("clip_split"), i18n("Restore Audio"), this, SLOT(slotSplitAV()),
1670                                     QIcon::fromTheme(QStringLiteral("document-new")), QKeySequence(), clipActionCategory);
1671     // "S" will be handled specifically to change the action name depending on current selection
1672     splitAudio->setData('S');
1673     splitAudio->setEnabled(false);
1674 
1675     QAction *extractClip = addAction(QStringLiteral("extract_clip"), i18n("Extract Clip"), this, SLOT(slotExtractClip()),
1676                                      QIcon::fromTheme(QStringLiteral("timeline-extract")), QKeySequence(), clipActionCategory);
1677     extractClip->setData('C');
1678     extractClip->setEnabled(false);
1679 
1680     QAction *extractToBin =
1681         addAction(QStringLiteral("save_to_bin"), i18n("Save Clip Part to Bin"), this, SLOT(slotSaveZoneToBin()), QIcon(), QKeySequence(), clipActionCategory);
1682     extractToBin->setData('C');
1683     extractToBin->setEnabled(false);
1684 
1685     QAction *switchEnable =
1686         addAction(QStringLiteral("clip_switch"), i18n("Disable Clip"), this, SLOT(slotSwitchClip()), QIcon(), QKeySequence(), clipActionCategory);
1687     // "W" will be handled specifically to change the action name depending on current selection
1688     switchEnable->setData('W');
1689     switchEnable->setEnabled(false);
1690 
1691     QAction *setAudioAlignReference = addAction(QStringLiteral("set_audio_align_ref"), i18n("Set Audio Reference"), this, SLOT(slotSetAudioAlignReference()),
1692                                                 QIcon(), QKeySequence(), clipActionCategory);
1693     // "A" as data means this action should only be available for clips with audio
1694     setAudioAlignReference->setData('A');
1695     setAudioAlignReference->setEnabled(false);
1696 
1697     QAction *alignAudio =
1698         addAction(QStringLiteral("align_audio"), i18n("Align Audio to Reference"), this, SLOT(slotAlignAudio()), QIcon(), QKeySequence(), clipActionCategory);
1699     // "A" as data means this action should only be available for clips with audio
1700     // alignAudio->setData('A');
1701     alignAudio->setEnabled(false);
1702 
1703     QAction *act = addAction(QStringLiteral("edit_item_duration"), i18n("Edit Duration"), this, SLOT(slotEditItemDuration()),
1704                              QIcon::fromTheme(QStringLiteral("measure")), QKeySequence(), clipActionCategory);
1705     act->setEnabled(false);
1706 
1707     act = addAction(QStringLiteral("edit_item_speed"), i18n("Change Speed"), this, SLOT(slotEditItemSpeed()), QIcon::fromTheme(QStringLiteral("speedometer")),
1708                     QKeySequence(), clipActionCategory);
1709     // "Q" as data means this action should only be available if the item is not endless and has no time remap
1710     act->setData('Q');
1711     act->setEnabled(false);
1712 
1713     act = addAction(QStringLiteral("edit_item_remap"), i18n("Time Remap"), this, SLOT(slotRemapItemTime()), QIcon::fromTheme(QStringLiteral("speedometer")),
1714                     QKeySequence(), clipActionCategory);
1715     // "R" as data means this action should only be available if the item is not endless and has no speed effect
1716     act->setData('R');
1717     act->setCheckable(true);
1718     act->setEnabled(false);
1719 
1720     act = addAction(QStringLiteral("clip_in_project_tree"), i18n("Clip in Project Bin"), this, SLOT(slotClipInProjectTree()),
1721                     QIcon::fromTheme(QStringLiteral("find-location")), QKeySequence(), clipActionCategory);
1722     act->setEnabled(false);
1723     // "C" as data means this action should only be available for clips - not for compositions
1724     act->setData('C');
1725 
1726     addAction(QStringLiteral("cut_timeline_clip"), i18n("Cut Clip"), this, SLOT(slotCutTimelineClip()), QIcon::fromTheme(QStringLiteral("edit-cut")),
1727               Qt::SHIFT | Qt::Key_R);
1728 
1729     addAction(QStringLiteral("cut_timeline_all_clips"), i18n("Cut All Clips"), this, SLOT(slotCutTimelineAllClips()),
1730               QIcon::fromTheme(QStringLiteral("edit-cut")), Qt::CTRL | Qt::SHIFT | Qt::Key_R);
1731 
1732     addAction(QStringLiteral("delete_timeline_clip"), i18n("Delete Selected Item"), this, SLOT(slotDeleteItem()),
1733               QIcon::fromTheme(QStringLiteral("edit-delete")), Qt::Key_Delete);
1734 
1735     QAction *resizeStart = new QAction(QIcon(), i18n("Resize Item Start"), this);
1736     addAction(QStringLiteral("resize_timeline_clip_start"), resizeStart, QKeySequence(Qt::Key_ParenLeft));
1737     connect(resizeStart, &QAction::triggered, this, &MainWindow::slotResizeItemStart);
1738 
1739     QAction *resizeEnd = new QAction(QIcon(), i18n("Resize Item End"), this);
1740     addAction(QStringLiteral("resize_timeline_clip_end"), resizeEnd, QKeySequence(Qt::Key_ParenRight));
1741     connect(resizeEnd, &QAction::triggered, this, &MainWindow::slotResizeItemEnd);
1742 
1743     QAction *pasteEffects = addAction(QStringLiteral("paste_effects"), i18n("Paste Effects"), this, SLOT(slotPasteEffects()),
1744                                       QIcon::fromTheme(QStringLiteral("edit-paste")), QKeySequence(), clipActionCategory);
1745     pasteEffects->setEnabled(false);
1746     // "C" as data means this action should only be available for clips - not for compositions
1747     pasteEffects->setData('C');
1748 
1749     QAction *delEffects = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete Effects"), this);
1750     addAction(QStringLiteral("delete_effects"), delEffects, QKeySequence(), clipActionCategory);
1751     delEffects->setEnabled(false);
1752     // "C" as data means this action should only be available for clips - not for compositions
1753     delEffects->setData('C');
1754     connect(delEffects, &QAction::triggered, this, [this]() { getCurrentTimeline()->controller()->deleteEffects(); });
1755 
1756     QAction *groupClip = addAction(QStringLiteral("group_clip"), i18n("Group Clips"), this, SLOT(slotGroupClips()),
1757                                    QIcon::fromTheme(QStringLiteral("object-group")), Qt::CTRL | Qt::Key_G, clipActionCategory);
1758     // "G" as data means this action should only be available for multiple items selection
1759     groupClip->setData('G');
1760     groupClip->setEnabled(false);
1761 
1762     QAction *ungroupClip = addAction(QStringLiteral("ungroup_clip"), i18n("Ungroup Clips"), this, SLOT(slotUnGroupClips()),
1763                                      QIcon::fromTheme(QStringLiteral("object-ungroup")), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_G), clipActionCategory);
1764     // "U" as data means this action should only be available if selection is a group
1765     ungroupClip->setData('U');
1766     ungroupClip->setEnabled(false);
1767 
1768     QAction *sentToSequence = addAction(QStringLiteral("send_sequence"), i18n("Create Sequence from Selection"), this, SLOT(slotCreateSequenceFromSelection()),
1769                                         QIcon::fromTheme(QStringLiteral("bookmark-new")), QKeySequence(), clipActionCategory);
1770     sentToSequence->setWhatsThis(
1771         xi18nc("@info:whatsthis", "Adds the clip(s) currently selected in the timeline to a new sequence clip that can be opened in another timeline tab."));
1772     sentToSequence->setData('G');
1773     sentToSequence->setEnabled(false);
1774 
1775     act = clipActionCategory->addAction(KStandardAction::Copy, this, SLOT(slotCopy()));
1776     act->setEnabled(false);
1777 
1778     KStandardAction::paste(this, SLOT(slotPaste()), actionCollection());
1779 
1780     // Keyframe actions
1781     m_assetPanel = new AssetPanel(this);
1782     connect(getBin(), &Bin::requestShowEffectStack, m_assetPanel, &AssetPanel::showEffectStack);
1783     KActionCategory *kfActions = new KActionCategory(i18n("Effect Keyframes"), actionCollection());
1784     addAction(QStringLiteral("keyframe_add"), i18n("Add/Remove Keyframe"), m_assetPanel, SLOT(slotAddRemoveKeyframe()),
1785               QIcon::fromTheme(QStringLiteral("keyframe-add")), QKeySequence(), kfActions);
1786     addAction(QStringLiteral("keyframe_next"), i18n("Go to next keyframe"), m_assetPanel, SLOT(slotNextKeyframe()),
1787               QIcon::fromTheme(QStringLiteral("keyframe-next")), QKeySequence(), kfActions);
1788     addAction(QStringLiteral("keyframe_previous"), i18n("Go to previous keyframe"), m_assetPanel, SLOT(slotPreviousKeyframe()),
1789               QIcon::fromTheme(QStringLiteral("keyframe-previous")), QKeySequence(), kfActions);
1790 
1791     /*act = KStandardAction::copy(this, SLOT(slotCopy()), actionCollection());
1792     clipActionCategory->addAction(KStandardAction::name(KStandardAction::Copy), act);
1793     act->setEnabled(false);
1794     act = KStandardAction::paste(this, SLOT(slotPaste()), actionCollection());
1795     clipActionCategory->addAction(KStandardAction::name(KStandardAction::Paste), act);
1796     act->setEnabled(false);*/
1797 
1798     kdenliveCategoryMap.insert(QStringLiteral("timelineselection"), clipActionCategory);
1799 
1800     addAction(QStringLiteral("insert_space"), i18n("Insert Space…"), this, SLOT(slotInsertSpace()));
1801     addAction(QStringLiteral("delete_space"), i18n("Remove Space"), this, SLOT(slotRemoveSpace()));
1802     addAction(QStringLiteral("delete_all_spaces"), i18n("Remove All Spaces After Cursor"), this, SLOT(slotRemoveAllSpacesInTrack()));
1803     addAction(QStringLiteral("delete_all_clips"), i18n("Remove All Clips After Cursor"), this, SLOT(slotRemoveAllClipsInTrack()));
1804     addAction(QStringLiteral("delete_space_all_tracks"), i18n("Remove Space in All Tracks"), this, SLOT(slotRemoveSpaceInAllTracks()));
1805 
1806     KActionCategory *timelineActions = new KActionCategory(i18n("Tracks"), actionCollection());
1807     QAction *insertTrack = new QAction(QIcon(), i18nc("@action", "Insert Track…"), this);
1808     connect(insertTrack, &QAction::triggered, this, &MainWindow::slotInsertTrack);
1809     timelineActions->addAction(QStringLiteral("insert_track"), insertTrack);
1810 
1811     QAction *autoTrackHeight = new QAction(QIcon(), i18n("Fit all Tracks in View"), this);
1812     autoTrackHeight->setCheckable(true);
1813     autoTrackHeight->setChecked(KdenliveSettings::autotrackheight());
1814     connect(autoTrackHeight, &QAction::triggered, this, &MainWindow::slotAutoTrackHeight);
1815     timelineActions->addAction(QStringLiteral("fit_all_tracks"), autoTrackHeight);
1816 
1817     QAction *masterEffectStack = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-composite")), i18n("Master effects"), this);
1818     connect(masterEffectStack, &QAction::triggered, this, [&]() {
1819         pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
1820         getCurrentTimeline()->controller()->showMasterEffects();
1821     });
1822     timelineActions->addAction(QStringLiteral("master_effects"), masterEffectStack);
1823 
1824     QAction *switchTrackTarget = new QAction(QIcon(), i18n("Switch Track Target Audio Stream"), this);
1825     connect(switchTrackTarget, &QAction::triggered, this, &MainWindow::slotSwitchTrackAudioStream);
1826     timelineActions->addAction(QStringLiteral("switch_target_stream"), switchTrackTarget);
1827     actionCollection()->setDefaultShortcut(switchTrackTarget, Qt::Key_Apostrophe);
1828 
1829     QAction *deleteTrack = new QAction(QIcon(), i18n("Delete Track…"), this);
1830     connect(deleteTrack, &QAction::triggered, this, &MainWindow::slotDeleteTrack);
1831     timelineActions->addAction(QStringLiteral("delete_track"), deleteTrack);
1832     deleteTrack->setData("delete_track");
1833 
1834     QAction *showAudio = new QAction(QIcon(), i18n("Show Record Controls"), this);
1835     connect(showAudio, &QAction::triggered, this, &MainWindow::slotShowTrackRec);
1836     timelineActions->addAction(QStringLiteral("show_track_record"), showAudio);
1837     showAudio->setCheckable(true);
1838     showAudio->setData("show_track_record");
1839 
1840     QAction *selectTrack = new QAction(QIcon(), i18n("Select All in Current Track"), this);
1841     connect(selectTrack, &QAction::triggered, this, &MainWindow::slotSelectTrack);
1842     timelineActions->addAction(QStringLiteral("select_track"), selectTrack);
1843 
1844     QAction *selectAll = KStandardAction::selectAll(this, SLOT(slotSelectAllTracks()), this);
1845     selectAll->setIcon(QIcon::fromTheme(QStringLiteral("edit-select-all")));
1846     selectAll->setShortcutContext(Qt::WidgetWithChildrenShortcut);
1847     timelineActions->addAction(QStringLiteral("select_all_tracks"), selectAll);
1848 
1849     QAction *unselectAll = KStandardAction::deselect(this, SLOT(slotUnselectAllTracks()), this);
1850     unselectAll->setIcon(QIcon::fromTheme(QStringLiteral("edit-select-none")));
1851     unselectAll->setShortcutContext(Qt::WidgetWithChildrenShortcut);
1852     timelineActions->addAction(QStringLiteral("unselect_all_tracks"), unselectAll);
1853 
1854     kdenliveCategoryMap.insert(QStringLiteral("timeline"), timelineActions);
1855 
1856     // Cached data management
1857     addAction(QStringLiteral("manage_cache"), i18n("Manage Cached Data…"), this, SLOT(slotManageCache()),
1858               QIcon::fromTheme(QStringLiteral("network-server-database")));
1859 
1860     QAction *disablePreview = new QAction(i18n("Disable Timeline Preview"), this);
1861     disablePreview->setCheckable(true);
1862     addAction(QStringLiteral("disable_preview"), disablePreview);
1863 
1864     addAction(QStringLiteral("add_guide"), i18n("Add/Remove Guide"), this, SLOT(slotAddGuide()), QIcon::fromTheme(QStringLiteral("list-add")), Qt::Key_G);
1865     addAction(QStringLiteral("delete_guide"), i18n("Delete Guide"), this, SLOT(slotDeleteGuide()), QIcon::fromTheme(QStringLiteral("edit-delete")));
1866     addAction(QStringLiteral("edit_guide"), i18n("Edit Guide…"), this, SLOT(slotEditGuide()), QIcon::fromTheme(QStringLiteral("document-properties")));
1867     addAction(QStringLiteral("search_guide"), i18n("Search Guide…"), this, SLOT(slotSearchGuide()), QIcon::fromTheme(QStringLiteral("edit-find")));
1868     addAction(QStringLiteral("export_guides"), i18n("Export Guides…"), this, SLOT(slotExportGuides()), QIcon::fromTheme(QStringLiteral("document-export")));
1869 
1870     QAction *lockGuides =
1871         addAction(QStringLiteral("lock_guides"), i18n("Guides Locked"), this, SLOT(slotLockGuides(bool)), QIcon::fromTheme(QStringLiteral("lock")));
1872     lockGuides->setCheckable(true);
1873     lockGuides->setChecked(KdenliveSettings::lockedGuides());
1874     lockGuides->setToolTip(i18n("Lock guides"));
1875     lockGuides->setWhatsThis(
1876         xi18nc("@info:whatsthis", "Lock guides. When locked, the guides won't move when using the spacer tool or inserting/removing blank in tracks."));
1877 
1878     addAction(QStringLiteral("delete_all_guides"), i18n("Delete All Guides"), this, SLOT(slotDeleteAllGuides()),
1879               QIcon::fromTheme(QStringLiteral("edit-delete")));
1880     addAction(QStringLiteral("add_subtitle"), i18n("Add Subtitle"), this, SLOT(slotAddSubtitle()), QIcon::fromTheme(QStringLiteral("list-add")),
1881               Qt::SHIFT | Qt::Key_S);
1882     addAction(QStringLiteral("disable_subtitle"), i18n("Disable Subtitle"), this, SLOT(slotDisableSubtitle()), QIcon::fromTheme(QStringLiteral("view-hidden")));
1883     addAction(QStringLiteral("lock_subtitle"), i18n("Lock Subtitle"), this, SLOT(slotLockSubtitle()), QIcon::fromTheme(QStringLiteral("lock")));
1884 
1885     addAction(QStringLiteral("manage_subtitle"), i18n("Manage Subtitles"), this, SLOT(slotManageSubtitle()),
1886               QIcon::fromTheme(QStringLiteral("settings-configure")));
1887     addAction(QStringLiteral("import_subtitle"), i18n("Import Subtitle File…"), this, SLOT(slotImportSubtitle()),
1888               QIcon::fromTheme(QStringLiteral("document-import")));
1889     addAction(QStringLiteral("export_subtitle"), i18n("Export Subtitle File…"), this, SLOT(slotExportSubtitle()),
1890               QIcon::fromTheme(QStringLiteral("document-export")));
1891     addAction(QStringLiteral("delete_subtitle_clip"), i18n("Delete Subtitle"), this, SLOT(slotDeleteItem()), QIcon::fromTheme(QStringLiteral("edit-delete")));
1892     addAction(QStringLiteral("audio_recognition"), i18n("Speech Recognition…"), this, SLOT(slotSpeechRecognition()),
1893               QIcon::fromTheme(QStringLiteral("autocorrection")));
1894 
1895     m_saveAction = KStandardAction::save(pCore->projectManager(), SLOT(saveFile()), actionCollection());
1896     m_saveAction->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
1897 
1898     QAction *const showMenuBarAction = KStandardAction::showMenubar(this, &MainWindow::showMenuBar, actionCollection());
1899     showMenuBarAction->setWhatsThis(xi18nc("@info:whatsthis", "This switches between having a <emphasis>Menubar</emphasis> "
1900                                                               "and having a <interface>Hamburger Menu</interface> button in the main Toolbar."));
1901 
1902     KStandardAction::quit(this, &MainWindow::close, actionCollection());
1903     KStandardAction::keyBindings(this, &MainWindow::slotEditKeys, actionCollection());
1904     KStandardAction::preferences(this, &MainWindow::slotPreferences, actionCollection());
1905     KStandardAction::configureNotifications(this, &MainWindow::configureNotifications, actionCollection());
1906     KStandardAction::fullScreen(this, &MainWindow::slotFullScreen, this, actionCollection());
1907 
1908     QAction *undo = KStandardAction::undo(m_commandStack, SLOT(undo()), actionCollection());
1909     undo->setEnabled(false);
1910     connect(m_commandStack, &QUndoGroup::canUndoChanged, undo, &QAction::setEnabled);
1911     connect(this, &MainWindow::enableUndo, this, [this, undo](bool enable) {
1912         if (enable) {
1913             enable = m_commandStack->activeStack()->canUndo();
1914         }
1915         undo->setEnabled(enable);
1916     });
1917 
1918     QAction *redo = KStandardAction::redo(m_commandStack, SLOT(redo()), actionCollection());
1919     redo->setEnabled(false);
1920     connect(m_commandStack, &QUndoGroup::canRedoChanged, redo, &QAction::setEnabled);
1921     connect(this, &MainWindow::enableUndo, this, [this, redo](bool enable) {
1922         if (enable) {
1923             enable = m_commandStack->activeStack()->canRedo();
1924         }
1925         redo->setEnabled(enable);
1926     });
1927 
1928     addAction(QStringLiteral("copy_debuginfo"), i18n("Copy Debug Information"), this, SLOT(slotCopyDebugInfo()), QIcon::fromTheme(QStringLiteral("edit-copy")));
1929 
1930     QAction *disableEffects = addAction(QStringLiteral("disable_timeline_effects"), i18n("Disable Timeline Effects"), pCore->projectManager(),
1931                                         SLOT(slotDisableTimelineEffects(bool)), QIcon::fromTheme(QStringLiteral("favorite")));
1932     disableEffects->setData("disable_timeline_effects");
1933     disableEffects->setCheckable(true);
1934     disableEffects->setChecked(false);
1935 
1936     addAction(QStringLiteral("switch_track_disabled"), i18n("Toggle Track Disabled"), pCore->projectManager(), SLOT(slotSwitchTrackDisabled()), QIcon(),
1937               Qt::SHIFT | Qt::Key_H, timelineActions);
1938     addAction(QStringLiteral("switch_track_lock"), i18n("Toggle Track Lock"), pCore->projectManager(), SLOT(slotSwitchTrackLock()), QIcon(),
1939               Qt::SHIFT | Qt::Key_L, timelineActions);
1940     addAction(QStringLiteral("switch_all_track_lock"), i18n("Toggle All Track Lock"), pCore->projectManager(), SLOT(slotSwitchAllTrackLock()), QIcon(),
1941               Qt::CTRL | Qt::SHIFT | Qt::Key_L, timelineActions);
1942     addAction(QStringLiteral("switch_track_target"), i18n("Toggle Track Target"), pCore->projectManager(), SLOT(slotSwitchTrackTarget()), QIcon(),
1943               Qt::SHIFT | Qt::Key_T, timelineActions);
1944     addAction(QStringLiteral("switch_active_target"), i18n("Toggle Track Active"), pCore->projectManager(), SLOT(slotSwitchTrackActive()), QIcon(), Qt::Key_A,
1945               timelineActions);
1946     addAction(QStringLiteral("switch_all_targets"), i18n("Toggle All Tracks Active"), pCore->projectManager(), SLOT(slotSwitchAllTrackActive()), QIcon(),
1947               Qt::SHIFT | Qt::Key_A, timelineActions);
1948     addAction(QStringLiteral("activate_all_targets"), i18n("Switch All Tracks Active"), pCore->projectManager(), SLOT(slotMakeAllTrackActive()), QIcon(),
1949               Qt::SHIFT | Qt::ALT | Qt::Key_A, timelineActions);
1950     addAction(QStringLiteral("restore_all_sources"), i18n("Restore Current Clip Target Tracks"), pCore->projectManager(), SLOT(slotRestoreTargetTracks()), {},
1951               {}, timelineActions);
1952     addAction(QStringLiteral("add_project_note"), i18n("Add Project Note"), pCore->projectManager(), SLOT(slotAddProjectNote()),
1953               QIcon::fromTheme(QStringLiteral("bookmark-new")), {}, timelineActions);
1954 
1955     // Build activate track shortcut sequences
1956     QList<int> keysequence{Qt::Key_1, Qt::Key_2, Qt::Key_3, Qt::Key_4, Qt::Key_5, Qt::Key_6, Qt::Key_7, Qt::Key_8, Qt::Key_9};
1957     for (int i = 1; i < 10; i++) {
1958         QAction *ac = new QAction(QIcon(), i18n("Select Audio Track %1", i), this);
1959         ac->setData(i - 1);
1960         connect(ac, &QAction::triggered, this, &MainWindow::slotActivateAudioTrackSequence);
1961         addAction(QString("activate_audio_%1").arg(i), ac, QKeySequence(Qt::ALT | keysequence[i - 1]), timelineActions);
1962         QAction *ac2 = new QAction(QIcon(), i18n("Select Video Track %1", i), this);
1963         ac2->setData(i - 1);
1964         connect(ac2, &QAction::triggered, this, &MainWindow::slotActivateVideoTrackSequence);
1965         addAction(QString("activate_video_%1").arg(i), ac2, QKeySequence(keysequence[i - 1]), timelineActions);
1966         QAction *ac3 = new QAction(QIcon(), i18n("Select Target %1", i), this);
1967         ac3->setData(i - 1);
1968         connect(ac3, &QAction::triggered, this, &MainWindow::slotActivateTarget);
1969         addAction(QString("activate_target_%1").arg(i), ac3, QKeySequence(Qt::CTRL | keysequence[i - 1]), timelineActions);
1970     }
1971 
1972     // Setup effects and transitions actions.
1973     KActionCategory *transitionActions = new KActionCategory(i18n("Transitions"), actionCollection());
1974     // m_transitions = new QAction*[transitions.count()];
1975     auto allTransitions = TransitionsRepository::get()->getNames();
1976     for (const auto &transition : qAsConst(allTransitions)) {
1977         auto *transAction = new QAction(transition.first, this);
1978         transAction->setData(transition.second);
1979         transAction->setIconVisibleInMenu(false);
1980         transitionActions->addAction("transition_" + transition.second, transAction);
1981     }
1982 
1983     // monitor actions
1984     addAction(QStringLiteral("extract_frame"), i18n("Extract Frame…"), pCore->monitorManager(), SLOT(slotExtractCurrentFrame()),
1985               QIcon::fromTheme(QStringLiteral("insert-image")));
1986 
1987     addAction(QStringLiteral("extract_frame_to_project"), i18n("Extract Frame to Project…"), pCore->monitorManager(), SLOT(slotExtractCurrentFrameToProject()),
1988               QIcon::fromTheme(QStringLiteral("insert-image")));
1989 }
1990 
1991 void MainWindow::saveOptions()
1992 {
1993     KdenliveSettings::self()->save();
1994 }
1995 
1996 bool MainWindow::readOptions()
1997 {
1998     KSharedConfigPtr config = KSharedConfig::openConfig();
1999     pCore->projectManager()->recentFilesAction()->loadEntries(KConfigGroup(config, "Recent Files"));
2000 
2001     if (KdenliveSettings::defaultprojectfolder().isEmpty()) {
2002         QDir dir(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation));
2003         dir.mkpath(QStringLiteral("."));
2004         KdenliveSettings::setDefaultprojectfolder(dir.absolutePath());
2005     }
2006     QFont ft = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
2007     // Default unit for timeline.qml objects size
2008     int baseUnit = qMax(28, qRound(QFontInfo(ft).pixelSize() * 1.8));
2009     if (KdenliveSettings::trackheight() == 0) {
2010         int trackHeight = qMax(50, int(2.2 * baseUnit + 6));
2011         KdenliveSettings::setTrackheight(trackHeight);
2012     }
2013     bool firstRun = false;
2014     KConfigGroup initialGroup(config, "version");
2015     if (!initialGroup.exists() || KdenliveSettings::sdlAudioBackend().isEmpty() || KdenliveSettings::kdenliverendererpath().isEmpty()) {
2016         // First run, check if user is on a KDE Desktop
2017         firstRun = true;
2018         // Define default video location for first run
2019         KRecentDirs::add(QStringLiteral(":KdenliveClipFolder"), QStandardPaths::writableLocation(QStandardPaths::MoviesLocation));
2020 
2021         // this is our first run, show Wizard
2022         QPointer<Wizard> w = new Wizard(true);
2023         if (w->exec() == QDialog::Accepted && w->isOk()) {
2024             w->adjustSettings();
2025             delete w;
2026         } else {
2027             delete w;
2028             ::exit(1);
2029         }
2030     } else if (!KdenliveSettings::ffmpegpath().isEmpty() && !QFile::exists(KdenliveSettings::ffmpegpath())) {
2031         // Invalid entry for FFmpeg, check system
2032         QPointer<Wizard> w = new Wizard(true);
2033         if (w->exec() == QDialog::Accepted && w->isOk()) {
2034             w->adjustSettings();
2035         }
2036         delete w;
2037     }
2038     if (firstRun) {
2039         if (TransitionsRepository::get()->exists(QStringLiteral("qtblend")) && TransitionsRepository::get()->getVersion(QStringLiteral("qtblend")) > 200) {
2040             KdenliveSettings::setPreferredcomposite(QStringLiteral("qtblend"));
2041         }
2042     }
2043     initialGroup.writeEntry("version", version);
2044     if (KdenliveSettings::guidesCategories().isEmpty()) {
2045         KdenliveSettings::setGuidesCategories(KdenliveDoc::getDefaultGuideCategories());
2046     }
2047     return firstRun;
2048 }
2049 
2050 void MainWindow::slotRunWizard()
2051 {
2052     QPointer<Wizard> w = new Wizard(false, this);
2053     if (w->exec() == QDialog::Accepted && w->isOk()) {
2054         w->adjustSettings();
2055     }
2056     delete w;
2057 }
2058 
2059 void MainWindow::slotRefreshProfiles()
2060 {
2061     KdenliveSettingsDialog *d = static_cast<KdenliveSettingsDialog *>(KConfigDialog::exists(QStringLiteral("settings")));
2062     if (d) {
2063         d->checkProfile();
2064     }
2065 }
2066 
2067 void MainWindow::slotEditProjectSettings(int ix)
2068 {
2069     KdenliveDoc *project = pCore->currentDoc();
2070     QPair<int, int> p = getCurrentTimeline()->getAvTracksCount();
2071     int channels = project->getDocumentProperty(QStringLiteral("audioChannels"), QStringLiteral("2")).toInt();
2072     ProjectSettings *w = new ProjectSettings(project, project->metadata(), getCurrentTimeline()->controller()->extractCompositionLumas(), p.second, p.first,
2073                                              channels, project->projectTempFolder(), true, !project->isModified(), this);
2074     if (ix > 0) {
2075         w->tabWidget->setCurrentIndex(ix);
2076     }
2077     connect(w, &ProjectSettings::disableProxies, this, &MainWindow::slotDisableProxies);
2078     // connect(w, SIGNAL(disablePreview()), pCore->projectManager()->currentTimeline(), SLOT(invalidateRange()));
2079     connect(w, &ProjectSettings::refreshProfiles, this, &MainWindow::slotRefreshProfiles);
2080 
2081     if (w->exec() == QDialog::Accepted) {
2082         QString profile = w->selectedProfile();
2083         bool modified = false;
2084         if (m_renderWidget) {
2085             m_renderWidget->updateDocumentPath();
2086         }
2087         const QStringList guidesCat = w->guidesCategories();
2088         if (guidesCat != project->guidesCategories()) {
2089             project->updateGuideCategories(guidesCat, w->remapGuidesCategories());
2090         }
2091         if (KdenliveSettings::videothumbnails() != w->enableVideoThumbs()) {
2092             slotSwitchVideoThumbs();
2093         }
2094         if (KdenliveSettings::audiothumbnails() != w->enableAudioThumbs()) {
2095             slotSwitchAudioThumbs();
2096         }
2097         if (project->getDocumentProperty(QStringLiteral("previewparameters")) != w->previewParams() ||
2098             project->getDocumentProperty(QStringLiteral("previewextension")) != w->previewExtension()) {
2099             modified = true;
2100             project->setDocumentProperty(QStringLiteral("previewparameters"), w->previewParams());
2101             project->setDocumentProperty(QStringLiteral("previewextension"), w->previewExtension());
2102             slotClearPreviewRender(false);
2103         }
2104 
2105         bool proxiesChanged = false;
2106         if (project->getDocumentProperty(QStringLiteral("proxyparams")) != w->proxyParams() ||
2107             project->getDocumentProperty(QStringLiteral("proxyextension")) != w->proxyExtension()) {
2108             modified = true;
2109             proxiesChanged = true;
2110             project->setDocumentProperty(QStringLiteral("proxyparams"), w->proxyParams());
2111             project->setDocumentProperty(QStringLiteral("proxyextension"), w->proxyExtension());
2112         }
2113         if (project->getDocumentProperty(QStringLiteral("externalproxyparams")) != w->externalProxyParams()) {
2114             modified = true;
2115             proxiesChanged = true;
2116             project->setDocumentProperty(QStringLiteral("externalproxyparams"), w->externalProxyParams());
2117         }
2118         if (proxiesChanged && pCore->projectItemModel()->hasProxies() &&
2119             KMessageBox::questionTwoActions(this, i18n("You have changed the proxy parameters. Do you want to recreate all proxy clips for this project?"), {},
2120                                             KGuiItem(i18nc("@action:button", "Recreate")),
2121                                             KGuiItem(i18nc("@action:button", "Continue without"))) == KMessageBox::PrimaryAction) {
2122             pCore->bin()->rebuildProxies();
2123         }
2124 
2125         if (project->getDocumentProperty(QStringLiteral("generateproxy")) != QString::number(int(w->generateProxy()))) {
2126             modified = true;
2127             project->setDocumentProperty(QStringLiteral("generateproxy"), QString::number(int(w->generateProxy())));
2128         }
2129         if (project->getDocumentProperty(QStringLiteral("proxyminsize")) != QString::number(w->proxyMinSize())) {
2130             modified = true;
2131             project->setDocumentProperty(QStringLiteral("proxyminsize"), QString::number(w->proxyMinSize()));
2132         }
2133         if (project->getDocumentProperty(QStringLiteral("generateimageproxy")) != QString::number(int(w->generateImageProxy()))) {
2134             modified = true;
2135             project->setDocumentProperty(QStringLiteral("generateimageproxy"), QString::number(int(w->generateImageProxy())));
2136         }
2137         if (project->getDocumentProperty(QStringLiteral("proxyimageminsize")) != QString::number(w->proxyImageMinSize())) {
2138             modified = true;
2139             project->setDocumentProperty(QStringLiteral("proxyimageminsize"), QString::number(w->proxyImageMinSize()));
2140         }
2141         if (project->getDocumentProperty(QStringLiteral("proxyimagesize")) != QString::number(w->proxyImageSize())) {
2142             modified = true;
2143             project->setDocumentProperty(QStringLiteral("proxyimagesize"), QString::number(w->proxyImageSize()));
2144         }
2145         if (project->getDocumentProperty(QStringLiteral("proxyresize")) != QString::number(w->proxyResize())) {
2146             modified = true;
2147             project->setDocumentProperty(QStringLiteral("proxyresize"), QString::number(w->proxyResize()));
2148         }
2149         if (QString::number(int(w->useProxy())) != project->getDocumentProperty(QStringLiteral("enableproxy"))) {
2150             project->setDocumentProperty(QStringLiteral("enableproxy"), QString::number(int(w->useProxy())));
2151             modified = true;
2152             slotUpdateProxySettings();
2153         }
2154         if (QString::number(int(w->useExternalProxy())) != project->getDocumentProperty(QStringLiteral("enableexternalproxy"))) {
2155             project->setDocumentProperty(QStringLiteral("enableexternalproxy"), QString::number(int(w->useExternalProxy())));
2156             modified = true;
2157         }
2158         if (w->metadata() != project->metadata()) {
2159             project->setMetadata(w->metadata());
2160             if (m_renderWidget) {
2161                 m_renderWidget->updateMetadataToolTip();
2162             }
2163         }
2164         QString newProjectFolder = w->storageFolder();
2165 
2166         if (w->docFolderAsStorageFolder()) {
2167             newProjectFolder = QFileInfo(project->url().toLocalFile()).absolutePath() + QStringLiteral("/cachefiles");
2168         }
2169         if (newProjectFolder.isEmpty()) {
2170             newProjectFolder = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
2171         }
2172         if (newProjectFolder != project->projectTempFolder()) {
2173             KMessageBox::ButtonCode answer;
2174             // Project folder changed:
2175             if (project->isModified()) {
2176                 answer = KMessageBox::warningContinueCancel(
2177                     this, i18n("The current project has not been saved.<br/>This will first save the project, then move "
2178                                "all temporary files from <br/><b>%1</b> to <b>%2</b>,<br>and the project file will be reloaded",
2179                                project->projectTempFolder(), newProjectFolder));
2180                 if (answer == KMessageBox::Continue) {
2181                     pCore->projectManager()->saveFile();
2182                 }
2183             } else {
2184                 answer = KMessageBox::warningContinueCancel(
2185                     this, i18n("This will move all temporary files from<br/><b>%1</b> to <b>%2</b>,<br/>the project file will then be reloaded",
2186                                project->projectTempFolder(), newProjectFolder));
2187             }
2188             if (answer == KMessageBox::Continue) {
2189                 // Proceed with move
2190                 QString documentId = QDir::cleanPath(project->getDocumentProperty(QStringLiteral("documentid")));
2191                 bool ok;
2192                 documentId.toLongLong(&ok, 10);
2193                 if (!ok || documentId.isEmpty()) {
2194                     KMessageBox::error(this, i18n("Cannot perform operation, invalid document id: %1", documentId));
2195                 } else {
2196                     QDir newDir(newProjectFolder);
2197                     QDir oldDir(project->projectTempFolder());
2198                     if (newDir.exists(documentId)) {
2199                         KMessageBox::error(this, i18n("Cannot perform operation, target directory already exists: %1", newDir.absoluteFilePath(documentId)));
2200                     } else {
2201                         // Proceed with the move
2202                         pCore->projectManager()->moveProjectData(oldDir.absoluteFilePath(documentId), newDir.absolutePath());
2203                     }
2204                 }
2205             }
2206         }
2207         if (pCore->getCurrentProfile()->path() != profile || project->profileChanged(profile)) {
2208             if (!qFuzzyCompare(pCore->getCurrentProfile()->fps() - ProfileRepository::get()->getProfile(profile)->fps(), 0.)) {
2209                 // Fps was changed, we save the project to an xml file with updated profile and reload project
2210                 // Check if blank project
2211                 if (project->url().fileName().isEmpty() && !project->isModified()) {
2212                     // Trying to switch project profile from an empty project
2213                     pCore->setCurrentProfile(profile);
2214                     pCore->projectManager()->newFile(profile, false);
2215                     return;
2216                 }
2217                 pCore->projectManager()->saveWithUpdatedProfile(profile);
2218             } else {
2219                 bool darChanged = !qFuzzyCompare(pCore->getCurrentProfile()->dar(), ProfileRepository::get()->getProfile(profile)->dar());
2220                 pCore->setCurrentProfile(profile);
2221                 pCore->projectManager()->slotResetProfiles(darChanged);
2222                 slotUpdateDocumentState(true);
2223             }
2224         } else if (modified) {
2225             project->setModified();
2226         }
2227     }
2228     delete w;
2229 }
2230 
2231 void MainWindow::slotDisableProxies()
2232 {
2233     pCore->currentDoc()->setDocumentProperty(QStringLiteral("enableproxy"), QString::number(false));
2234     pCore->currentDoc()->setModified();
2235     slotUpdateProxySettings();
2236 }
2237 
2238 void MainWindow::slotStopRenderProject()
2239 {
2240     if (m_renderWidget) {
2241         m_renderWidget->slotAbortCurrentJob();
2242     }
2243 }
2244 
2245 void MainWindow::updateProjectPath(const QString &path)
2246 {
2247     if (m_renderWidget) {
2248         m_renderWidget->resetRenderPath(path);
2249     } else {
2250         // Clear render name as project url changed
2251         QMap<QString, QString> renderProps;
2252         renderProps.insert(QStringLiteral("renderurl"), QString());
2253         slotSetDocumentRenderProfile(renderProps);
2254     }
2255 }
2256 
2257 void MainWindow::slotRenderProject()
2258 {
2259     KdenliveDoc *project = pCore->currentDoc();
2260 
2261     if (!m_renderWidget && project) {
2262         m_renderWidget = new RenderWidget(project->useProxy(), this);
2263         connect(m_renderWidget, &RenderWidget::shutdown, this, &MainWindow::slotShutdown);
2264         connect(m_renderWidget, &RenderWidget::selectedRenderProfile, this, &MainWindow::slotSetDocumentRenderProfile);
2265         connect(m_renderWidget, &RenderWidget::abortProcess, this, &MainWindow::abortRenderJob);
2266         connect(pCore.get(), &Core::gotMissingClipsCount, m_renderWidget, &RenderWidget::updateMissingClipsCount);
2267         connect(this, &MainWindow::updateRenderWidgetProfile, m_renderWidget, &RenderWidget::adjustViewToProfile);
2268         m_renderWidget->setGuides(project->getGuideModel(getCurrentTimeline()->getUuid()));
2269         m_renderWidget->updateDocumentPath();
2270         m_renderWidget->setRenderProfile(project->getRenderProperties());
2271     }
2272 
2273     slotCheckRenderStatus();
2274     if (m_renderWidget) {
2275         m_renderWidget->showNormal();
2276     }
2277 
2278     // What are the following lines supposed to do?
2279     // m_renderWidget->enableAudio(false);
2280     // m_renderWidget->export_audio;
2281 }
2282 
2283 void MainWindow::slotCheckRenderStatus()
2284 {
2285     // Make sure there are no missing clips
2286     // TODO
2287     /*if (m_renderWidget)
2288         m_renderWidget->missingClips(pCore->bin()->hasMissingClips());*/
2289 }
2290 
2291 void MainWindow::setRenderingProgress(const QString &url, int progress, int frame)
2292 {
2293     Q_EMIT setRenderProgress(progress);
2294     if (m_renderWidget) {
2295         m_renderWidget->setRenderProgress(url, progress, frame);
2296     }
2297 }
2298 
2299 void MainWindow::setRenderingFinished(const QString &url, int status, const QString &error)
2300 {
2301     Q_EMIT setRenderProgress(100);
2302     if (m_renderWidget) {
2303         m_renderWidget->setRenderStatus(url, status, error);
2304     }
2305 }
2306 
2307 void MainWindow::addProjectClip(const QString &url, const QString &folder)
2308 {
2309     if (pCore->currentDoc()) {
2310         QStringList ids = pCore->projectItemModel()->getClipByUrl(QFileInfo(url));
2311         if (!ids.isEmpty()) {
2312             // Clip is already in project bin, abort
2313             return;
2314         }
2315         ClipCreator::createClipFromFile(url, folder, pCore->projectItemModel());
2316     }
2317 }
2318 
2319 void MainWindow::addTimelineClip(const QString &url)
2320 {
2321     if (pCore->currentDoc()) {
2322         QStringList ids = pCore->projectItemModel()->getClipByUrl(QFileInfo(url));
2323         if (!ids.isEmpty()) {
2324             pCore->selectBinClip(ids.constFirst());
2325             slotInsertClipInsert();
2326         }
2327     }
2328 }
2329 
2330 void MainWindow::scriptRender(const QString &url)
2331 {
2332     Q_UNUSED(url)
2333     slotRenderProject();
2334     m_renderWidget->slotPrepareExport(true);
2335 }
2336 
2337 #ifndef NODBUS
2338 void MainWindow::exitApp()
2339 {
2340     QApplication::exit(0);
2341 }
2342 #endif
2343 
2344 void MainWindow::slotCleanProject()
2345 {
2346     if (KMessageBox::warningContinueCancel(this, i18n("This will remove all unused clips from your project."), i18n("Clean up project")) ==
2347         KMessageBox::Cancel) {
2348         return;
2349     }
2350     pCore->bin()->cleanupUnused();
2351 }
2352 
2353 void MainWindow::slotUpdateMousePosition(int pos, int duration)
2354 {
2355     if (pCore->currentDoc()) {
2356         if (duration < 0) {
2357             duration = getCurrentTimeline()->controller()->duration();
2358         }
2359         if (pos >= 0) {
2360             m_mousePosition = pos;
2361         }
2362         switch (m_timeFormatButton->currentItem()) {
2363         case 0:
2364             m_timeFormatButton->setText(pCore->currentDoc()->timecode().getTimecodeFromFrames(m_mousePosition) + QStringLiteral(" / ") +
2365                                         pCore->currentDoc()->timecode().getTimecodeFromFrames(duration));
2366             break;
2367         default:
2368             m_timeFormatButton->setText(QStringLiteral("%1 / %2").arg(m_mousePosition, 6, 10, QLatin1Char('0')).arg(duration, 6, 10, QLatin1Char('0')));
2369         }
2370     }
2371 }
2372 
2373 void MainWindow::slotUpdateProjectDuration(int duration)
2374 {
2375     if (pCore->currentDoc()) {
2376         slotUpdateMousePosition(-1, duration);
2377     }
2378     if (m_renderWidget) {
2379         m_renderWidget->projectDurationChanged(duration);
2380     }
2381 }
2382 
2383 void MainWindow::slotUpdateZoneDuration(int duration)
2384 {
2385     if (m_renderWidget) {
2386         m_renderWidget->zoneDurationChanged(duration);
2387     }
2388 }
2389 
2390 void MainWindow::slotUpdateDocumentState(bool modified)
2391 {
2392     m_timelineTabs->updateWindowTitle();
2393     setWindowModified(modified);
2394     m_saveAction->setEnabled(modified);
2395 }
2396 
2397 void MainWindow::connectDocument()
2398 {
2399     KdenliveDoc *project = pCore->currentDoc();
2400     connect(project, &KdenliveDoc::startAutoSave, pCore->projectManager(), &ProjectManager::slotStartAutoSave);
2401     connect(project, &KdenliveDoc::reloadEffects, this, &MainWindow::slotReloadEffects);
2402     KdenliveSettings::setProject_fps(pCore->getCurrentFps());
2403     slotSwitchTimelineZone(project->getDocumentProperty(QStringLiteral("enableTimelineZone")).toInt() == 1);
2404     // update track compositing
2405     bool compositing = project->getDocumentProperty(QStringLiteral("compositing"), QStringLiteral("1")).toInt() > 0;
2406     Q_EMIT project->updateCompositionMode(compositing);
2407     getCurrentTimeline()->controller()->switchCompositing(compositing);
2408     slotUpdateProjectDuration(getCurrentTimeline()->model()->duration() - 1);
2409     const QUuid uuid = getCurrentTimeline()->getUuid();
2410 
2411     int activeTrackPosition = project->getSequenceProperty(uuid, QStringLiteral("activeTrack"), QString::number(-1)).toInt();
2412     if (activeTrackPosition == -2) {
2413         // Subtitle model track always has ID == -2
2414         getCurrentTimeline()->controller()->setActiveTrack(-2);
2415     } else if (activeTrackPosition > -1 && activeTrackPosition < getCurrentTimeline()->model()->getTracksCount()) {
2416         // otherwise, convert the position to a track ID
2417         getCurrentTimeline()->controller()->setActiveTrack(getCurrentTimeline()->model()->getTrackIndexFromPosition(activeTrackPosition));
2418     } else {
2419         qWarning() << "[BUG] \"activeTrack\" property is" << activeTrackPosition << "but track count is only"
2420                    << getCurrentTimeline()->model()->getTracksCount();
2421         // set it to some valid track instead
2422         getCurrentTimeline()->controller()->setActiveTrack(getCurrentTimeline()->model()->getTrackIndexFromPosition(0));
2423     }
2424 
2425     m_clipMonitor->updateDocumentUuid();
2426     connect(m_projectMonitor, &Monitor::multitrackView, getCurrentTimeline()->controller(), &TimelineController::slotMultitrackView, Qt::UniqueConnection);
2427     connect(m_projectMonitor, &Monitor::activateTrack, getCurrentTimeline()->controller(), &TimelineController::activateTrackAndSelect, Qt::UniqueConnection);
2428     connect(getCurrentTimeline()->controller(), &TimelineController::timelineClipSelected, this, [&](bool selected) {
2429         m_loopClip->setEnabled(selected);
2430         Q_EMIT pCore->library()->enableAddSelection(selected);
2431     });
2432     connect(pCore->library(), &LibraryWidget::saveTimelineSelection, getCurrentTimeline()->controller(), &TimelineController::saveTimelineSelection,
2433             Qt::UniqueConnection);
2434     connect(pCore->mixer(), &MixerManager::purgeCache, m_projectMonitor, &Monitor::purgeCache);
2435     connect(m_projectMonitor, &Monitor::zoneUpdated, project, [project](const QPoint &) { project->setModified(); });
2436     connect(m_clipMonitor, &Monitor::zoneUpdated, project, [project](const QPoint &) { project->setModified(); });
2437     connect(project, &KdenliveDoc::docModified, this, &MainWindow::slotUpdateDocumentState);
2438 
2439     if (m_renderWidget) {
2440         slotCheckRenderStatus();
2441         m_renderWidget->setGuides(pCore->currentDoc()->getGuideModel(uuid));
2442         m_renderWidget->updateDocumentPath();
2443         m_renderWidget->setRenderProfile(project->getRenderProperties());
2444         m_renderWidget->updateMetadataToolTip();
2445     }
2446 
2447     m_commandStack->setActiveStack(project->commandStack().get());
2448     m_timelineTabs->updateWindowTitle();
2449     setWindowModified(project->isModified());
2450     m_saveAction->setEnabled(project->isModified());
2451     m_normalEditTool->setChecked(true);
2452     connect(m_projectMonitor, &Monitor::durationChanged, this, &MainWindow::slotUpdateProjectDuration);
2453     connect(m_projectMonitor, &Monitor::zoneDurationChanged, this, &MainWindow::slotUpdateZoneDuration);
2454     connect(m_effectList2, &EffectListWidget::reloadFavorites, getCurrentTimeline(), &TimelineWidget::updateEffectFavorites);
2455     connect(m_compositionList, &TransitionListWidget::reloadFavorites, getCurrentTimeline(), &TimelineWidget::updateTransitionFavorites);
2456     connect(pCore->bin(), &Bin::processDragEnd, getCurrentTimeline(), &TimelineWidget::endDrag);
2457 
2458     // Load master effect zones
2459     getCurrentTimeline()->controller()->updateMasterZones(getCurrentTimeline()->model()->getMasterEffectZones());
2460 
2461     m_buttonSelectTool->setChecked(true);
2462     connect(m_projectMonitorDock, &QDockWidget::visibilityChanged, m_projectMonitor, &Monitor::slotRefreshMonitor, Qt::UniqueConnection);
2463     connect(m_clipMonitorDock, &QDockWidget::visibilityChanged, m_clipMonitor, &Monitor::slotRefreshMonitor, Qt::UniqueConnection);
2464     pCore->guidesList()->reset();
2465     getCurrentTimeline()->focusTimeline();
2466 }
2467 
2468 void MainWindow::slotEditKeys()
2469 {
2470     KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this);
2471 
2472 #if KXMLGUI_VERSION >= QT_VERSION_CHECK(5, 98, 0)
2473     KNSWidgets::Action *downloadKeybordSchemes =
2474         new KNSWidgets::Action(i18n("Download New Keyboard Schemes…"), QStringLiteral(":data/kdenlive_keyboardschemes.knsrc"), this);
2475     connect(downloadKeybordSchemes, &KNSWidgets::Action::dialogFinished, this, [&](const QList<KNSCore::Entry> &changedEntries) {
2476         if (changedEntries.count() > 0) {
2477             dialog.refreshSchemes();
2478         }
2479     });
2480     dialog.addActionToSchemesMoreButton(downloadKeybordSchemes);
2481 #else
2482     // Find the combobox inside KShortcutsDialog for choosing keyboard scheme
2483     QComboBox *schemesList = nullptr;
2484     for (QLabel *label : dialog.findChildren<QLabel *>()) {
2485         if (label->text() == i18n("Current scheme:")) {
2486             schemesList = qobject_cast<QComboBox *>(label->buddy());
2487             break;
2488         }
2489     }
2490     // If scheme choosing combobox was found, find the "More Actions" button in the same
2491     // dialog that provides a dropdown menu with additional actions, and add
2492     // "Download New Keyboard Schemes…" button into that menu
2493     if (schemesList) {
2494         for (QPushButton *button : dialog.findChildren<QPushButton *>()) {
2495             if (button->text() == i18n("More Actions")) {
2496                 QMenu *moreActionsMenu = button->menu();
2497                 if (moreActionsMenu) {
2498                     moreActionsMenu->addAction(i18n("Download New Keyboard Schemes…"), this, [this, schemesList] { slotGetNewKeyboardStuff(schemesList); });
2499                 }
2500                 break;
2501             }
2502         }
2503     } else {
2504         qWarning() << "Could not get list of schemes. Downloading new schemes is not available.";
2505     }
2506 #endif
2507     dialog.addCollection(actionCollection(), i18nc("general keyboard shortcuts", "General"));
2508     // Update the shortcut conflicts list bewtween mainwindow and media browser
2509     connect(&dialog, &KShortcutsDialog::saved, pCore->mediaBrowser(), &MediaBrowser::detectShortcutConflicts);
2510     dialog.configure();
2511 }
2512 
2513 void MainWindow::slotPreferences()
2514 {
2515     slotShowPreferencePage(Kdenlive::NoPage);
2516 }
2517 
2518 void MainWindow::slotShowPreferencePage(Kdenlive::ConfigPage page, int option)
2519 {
2520     /*
2521      * An instance of your dialog could be already created and could be
2522      * cached, in which case you want to display the cached dialog
2523      * instead of creating another one
2524      */
2525     if (KConfigDialog::showDialog(QStringLiteral("settings"))) {
2526         KdenliveSettingsDialog *d = static_cast<KdenliveSettingsDialog *>(KConfigDialog::exists(QStringLiteral("settings")));
2527         if (page != Kdenlive::NoPage) {
2528             d->showPage(page, option);
2529         }
2530         return;
2531     }
2532 
2533     // KConfigDialog didn't find an instance of this dialog, so lets
2534     // create it :
2535 
2536     // Get the mappable actions in localized form
2537     QMap<QString, QString> actions;
2538     KActionCollection *collection = actionCollection();
2539     for (const QString &action_name : qAsConst(m_actionNames)) {
2540         const QString action_text = KLocalizedString::removeAcceleratorMarker(collection->action(action_name)->text());
2541         actions[action_text] = action_name;
2542     }
2543 
2544     auto *dialog = new KdenliveSettingsDialog(actions, m_gpuAllowed, this);
2545     connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::updateConfiguration);
2546     connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::configurationChanged);
2547     connect(dialog, &KdenliveSettingsDialog::doResetConsumer, this, [this](bool fullReset) {
2548         m_scaleGroup->setEnabled(!KdenliveSettings::external_display());
2549         pCore->projectManager()->slotResetConsumers(fullReset);
2550     });
2551     connect(dialog, &KdenliveSettingsDialog::checkTabPosition, this, &MainWindow::slotCheckTabPosition);
2552     connect(dialog, &KdenliveSettingsDialog::restartKdenlive, this, &MainWindow::slotRestart);
2553     connect(dialog, &KdenliveSettingsDialog::updateLibraryFolder, pCore.get(), &Core::updateLibraryPath);
2554     connect(dialog, &KdenliveSettingsDialog::audioThumbFormatChanged, m_timelineTabs, &TimelineTabs::audioThumbFormatChanged);
2555     connect(dialog, &KdenliveSettingsDialog::resetView, this, &MainWindow::resetTimelineTracks);
2556     connect(dialog, &KdenliveSettingsDialog::updateMonitorBg, [&]() { pCore->monitorManager()->updateBgColor(); });
2557     connect(dialog, &KdenliveSettingsDialog::resetAudioMonitoring, pCore.get(), &Core::resetAudioMonitoring);
2558 
2559     dialog->show();
2560     if (page != Kdenlive::NoPage) {
2561         dialog->showPage(page, option);
2562     }
2563 }
2564 
2565 void MainWindow::slotCheckTabPosition()
2566 {
2567     int pos = tabPosition(Qt::LeftDockWidgetArea);
2568     if (KdenliveSettings::tabposition() != pos) {
2569         setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition(KdenliveSettings::tabposition()));
2570     }
2571 }
2572 
2573 void MainWindow::slotRestart(bool clean)
2574 {
2575     if (clean) {
2576         if (KMessageBox::warningContinueCancel(this,
2577                                                i18n("This will delete Kdenlive's configuration file and restart the application. Do you want to proceed?"),
2578                                                i18nc("@title:window", "Reset Configuration")) != KMessageBox::Continue) {
2579             return;
2580         }
2581     }
2582     cleanRestart(clean);
2583 }
2584 
2585 void MainWindow::cleanRestart(bool clean)
2586 {
2587     m_exitCode = clean ? EXIT_CLEAN_RESTART : EXIT_RESTART;
2588     QApplication::closeAllWindows();
2589 }
2590 
2591 void MainWindow::closeEvent(QCloseEvent *event)
2592 {
2593     KXmlGuiWindow::closeEvent(event);
2594     if (event->isAccepted()) {
2595         QApplication::exit(m_exitCode);
2596         return;
2597     }
2598 }
2599 
2600 void MainWindow::updateConfiguration()
2601 {
2602     // TODO: we should apply settings to all projects, not only the current one
2603     m_buttonAudioThumbs->setChecked(KdenliveSettings::audiothumbnails());
2604     m_buttonVideoThumbs->setChecked(KdenliveSettings::videothumbnails());
2605     m_buttonShowMarkers->setChecked(KdenliveSettings::showmarkers());
2606 
2607     // Update list of transcoding profiles
2608     buildDynamicActions();
2609     loadClipActions();
2610 }
2611 
2612 void MainWindow::slotSwitchVideoThumbs()
2613 {
2614     KdenliveSettings::setVideothumbnails(!KdenliveSettings::videothumbnails());
2615     Q_EMIT m_timelineTabs->showThumbnailsChanged();
2616     m_buttonVideoThumbs->setChecked(KdenliveSettings::videothumbnails());
2617 }
2618 
2619 void MainWindow::slotSwitchAudioThumbs()
2620 {
2621     KdenliveSettings::setAudiothumbnails(!KdenliveSettings::audiothumbnails());
2622     pCore->bin()->checkAudioThumbs();
2623     Q_EMIT m_timelineTabs->showAudioThumbnailsChanged();
2624     m_buttonAudioThumbs->setChecked(KdenliveSettings::audiothumbnails());
2625 }
2626 
2627 void MainWindow::slotSwitchMarkersComments()
2628 {
2629     KdenliveSettings::setShowmarkers(!KdenliveSettings::showmarkers());
2630     Q_EMIT getCurrentTimeline()->controller()->showMarkersChanged();
2631     m_buttonShowMarkers->setChecked(KdenliveSettings::showmarkers());
2632 }
2633 
2634 void MainWindow::slotSwitchSnap()
2635 {
2636     KdenliveSettings::setSnaptopoints(!KdenliveSettings::snaptopoints());
2637     m_buttonSnap->setChecked(KdenliveSettings::snaptopoints());
2638     Q_EMIT getCurrentTimeline()->controller()->snapChanged();
2639 }
2640 
2641 void MainWindow::slotShowTimelineTags()
2642 {
2643     KdenliveSettings::setTagsintimeline(!KdenliveSettings::tagsintimeline());
2644     m_buttonTimelineTags->setChecked(KdenliveSettings::tagsintimeline());
2645     // Reset view to update timeline colors
2646     getCurrentTimeline()->model()->_resetView();
2647 }
2648 
2649 void MainWindow::slotDeleteItem()
2650 {
2651     if (QApplication::focusWidget() != nullptr) {
2652         for (auto &bin : m_binWidgets) {
2653             if (bin->isAncestorOf(QApplication::focusWidget())) {
2654                 bin->slotDeleteClip();
2655                 return;
2656             }
2657         }
2658     }
2659     if (QApplication::focusWidget() != nullptr && pCore->textEditWidget()->isAncestorOf(QApplication::focusWidget())) {
2660         pCore->textEditWidget()->deleteItem();
2661     } else {
2662         QWidget *widget = QApplication::focusWidget();
2663         while ((widget != nullptr) && widget != this) {
2664             if (widget == m_effectStackDock) {
2665                 m_assetPanel->deleteCurrentEffect();
2666                 return;
2667             }
2668             if (widget == pCore->guidesList()) {
2669                 pCore->guidesList()->removeGuide();
2670                 return;
2671             }
2672             widget = widget->parentWidget();
2673         }
2674 
2675         // effect stack has no focus
2676         getCurrentTimeline()->controller()->deleteSelectedClips();
2677     }
2678 }
2679 
2680 void MainWindow::slotAddClipMarker()
2681 {
2682     std::shared_ptr<ProjectClip> clip(nullptr);
2683     GenTime pos;
2684     if (m_projectMonitor->isActive()) {
2685         getCurrentTimeline()->controller()->addMarker();
2686         return;
2687     } else {
2688         clip = m_clipMonitor->currentController();
2689         pos = GenTime(m_clipMonitor->position(), pCore->getCurrentFps());
2690     }
2691     if (!clip) {
2692         m_messageLabel->setMessage(i18n("Cannot find clip to add marker"), ErrorMessage);
2693         return;
2694     }
2695     clip->getMarkerModel()->editMarkerGui(pos, this, true, clip.get());
2696 }
2697 
2698 void MainWindow::slotDeleteClipMarker(bool allowGuideDeletion)
2699 {
2700     std::shared_ptr<ProjectClip> clip(nullptr);
2701     GenTime pos;
2702     if (m_projectMonitor->isActive()) {
2703         getCurrentTimeline()->controller()->deleteMarker();
2704         return;
2705     } else {
2706         clip = m_clipMonitor->currentController();
2707         pos = GenTime(m_clipMonitor->position(), pCore->getCurrentFps());
2708     }
2709     if (!clip) {
2710         m_messageLabel->setMessage(i18n("Cannot find clip to remove marker"), ErrorMessage);
2711         return;
2712     }
2713 
2714     bool markerFound = false;
2715     clip->getMarkerModel()->getMarker(pos, &markerFound);
2716     if (!markerFound) {
2717         if (allowGuideDeletion && m_projectMonitor->isActive()) {
2718             slotDeleteGuide();
2719         } else {
2720             m_messageLabel->setMessage(i18n("No marker found at cursor time"), ErrorMessage);
2721         }
2722         return;
2723     }
2724     clip->getMarkerModel()->removeMarker(pos);
2725 }
2726 
2727 void MainWindow::slotDeleteAllClipMarkers()
2728 {
2729     std::shared_ptr<ProjectClip> clip(nullptr);
2730     if (m_projectMonitor->isActive()) {
2731         getCurrentTimeline()->controller()->deleteAllMarkers();
2732         return;
2733     } else {
2734         clip = m_clipMonitor->currentController();
2735     }
2736     if (!clip) {
2737         m_messageLabel->setMessage(i18n("Cannot find clip to remove marker"), ErrorMessage);
2738         return;
2739     }
2740     bool ok = clip->getMarkerModel()->removeAllMarkers();
2741     if (!ok) {
2742         m_messageLabel->setMessage(i18n("An error occurred while deleting markers"), ErrorMessage);
2743         return;
2744     }
2745 }
2746 
2747 void MainWindow::slotEditClipMarker()
2748 {
2749     std::shared_ptr<ProjectClip> clip(nullptr);
2750     GenTime pos;
2751     if (m_projectMonitor->isActive()) {
2752         getCurrentTimeline()->controller()->editMarker();
2753         return;
2754     } else {
2755         clip = m_clipMonitor->currentController();
2756         pos = GenTime(m_clipMonitor->position(), pCore->getCurrentFps());
2757     }
2758     if (!clip) {
2759         m_messageLabel->setMessage(i18n("Cannot find clip to edit marker"), ErrorMessage);
2760         return;
2761     }
2762 
2763     bool markerFound = false;
2764     clip->getMarkerModel()->getMarker(pos, &markerFound);
2765     if (!markerFound) {
2766         m_messageLabel->setMessage(i18n("No marker found at cursor time"), ErrorMessage);
2767         return;
2768     }
2769 
2770     clip->getMarkerModel()->editMarkerGui(pos, this, false, clip.get());
2771     // Focus back clip monitor
2772     m_clipMonitor->setFocus();
2773 }
2774 
2775 void MainWindow::slotAddMarkerGuideQuickly()
2776 {
2777     if (!getCurrentTimeline() || !pCore->currentDoc()) {
2778         return;
2779     }
2780 
2781     if (m_clipMonitor->isActive()) {
2782         pCore->bin()->addClipMarker(m_clipMonitor->activeClipId(), {m_clipMonitor->position()});
2783     } else {
2784         int selectedClip = getCurrentTimeline()->controller()->getMainSelectedItem();
2785         if (selectedClip == -1) {
2786             // Add timeline guide
2787             getCurrentTimeline()->controller()->switchGuide();
2788         } else {
2789             // Add marker to main clip
2790             getCurrentTimeline()->controller()->addQuickMarker(selectedClip);
2791         }
2792     }
2793 }
2794 
2795 void MainWindow::slotAddGuide()
2796 {
2797     getCurrentTimeline()->controller()->switchGuide(-1, false, true);
2798 }
2799 
2800 void MainWindow::slotInsertSpace()
2801 {
2802     getCurrentTimeline()->controller()->insertSpace();
2803 }
2804 
2805 void MainWindow::slotRemoveSpace()
2806 {
2807     getCurrentTimeline()->controller()->removeSpace(-1, -1, false);
2808 }
2809 
2810 void MainWindow::slotRemoveSpaceInAllTracks()
2811 {
2812     getCurrentTimeline()->controller()->removeSpace(-1, -1, true);
2813 }
2814 
2815 void MainWindow::slotRemoveAllSpacesInTrack()
2816 {
2817     getCurrentTimeline()->controller()->removeTrackSpaces(-1, -1);
2818 }
2819 
2820 void MainWindow::slotRemoveAllClipsInTrack()
2821 {
2822     getCurrentTimeline()->controller()->removeTrackClips(-1, -1);
2823 }
2824 
2825 void MainWindow::slotSeparateAudioChannel()
2826 {
2827     KdenliveSettings::setDisplayallchannels(!KdenliveSettings::displayallchannels());
2828     Q_EMIT getCurrentTimeline()->controller()->audioThumbFormatChanged();
2829     if (m_clipMonitor) {
2830         m_clipMonitor->refreshAudioThumbs();
2831     }
2832 }
2833 
2834 void MainWindow::slotAutoTrackHeight(bool enable)
2835 {
2836     KdenliveSettings::setAutotrackheight(enable);
2837     Q_EMIT pCore->autoTrackHeight(enable);
2838 }
2839 
2840 void MainWindow::slotNormalizeAudioChannel()
2841 {
2842     KdenliveSettings::setNormalizechannels(!KdenliveSettings::normalizechannels());
2843     Q_EMIT getCurrentTimeline()->controller()->audioThumbNormalizeChanged();
2844     if (m_clipMonitor) {
2845         m_clipMonitor->normalizeAudioThumbs();
2846     }
2847 }
2848 
2849 void MainWindow::slotInsertTrack()
2850 {
2851     pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
2852     getCurrentTimeline()->controller()->beginAddTrack(-1);
2853 }
2854 
2855 void MainWindow::slotDeleteTrack()
2856 {
2857     pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
2858     getCurrentTimeline()->controller()->deleteMultipleTracks(-1);
2859 }
2860 
2861 void MainWindow::slotSwitchTrackAudioStream()
2862 {
2863     getCurrentTimeline()->showTargetMenu();
2864 }
2865 
2866 void MainWindow::slotShowTrackRec(bool checked)
2867 {
2868     if (checked) {
2869         pCore->mixer()->monitorAudio(getCurrentTimeline()->controller()->activeTrack(), checked);
2870     } else {
2871         pCore->mixer()->monitorAudio(pCore->mixer()->recordTrack(), false);
2872     }
2873 }
2874 
2875 void MainWindow::slotSelectTrack()
2876 {
2877     getCurrentTimeline()->controller()->selectCurrentTrack();
2878 }
2879 
2880 void MainWindow::slotSelectAllTracks()
2881 {
2882     if (QApplication::focusWidget() != nullptr) {
2883         if (QApplication::focusWidget()->parentWidget() != nullptr) {
2884             for (auto &bin : m_binWidgets) {
2885                 if (bin->isAncestorOf(QApplication::focusWidget())) {
2886                     bin->selectAll();
2887                     return;
2888                 }
2889             }
2890         }
2891         if (QApplication::focusWidget()->objectName() == QLatin1String("guides_list")) {
2892             pCore->guidesList()->selectAll();
2893             return;
2894         }
2895     }
2896     getCurrentTimeline()->controller()->selectAll();
2897 }
2898 
2899 void MainWindow::slotUnselectAllTracks()
2900 {
2901     getCurrentTimeline()->model()->requestClearSelection();
2902 }
2903 
2904 void MainWindow::slotEditGuide()
2905 {
2906     getCurrentTimeline()->controller()->editGuide();
2907 }
2908 
2909 void MainWindow::slotSearchGuide()
2910 {
2911     pCore->guidesList()->filter_line->setFocus();
2912 }
2913 
2914 void MainWindow::slotExportGuides()
2915 {
2916     pCore->currentDoc()
2917         ->getGuideModel(getCurrentTimeline()->getUuid())
2918         ->exportGuidesGui(this, GenTime(getCurrentTimeline()->controller()->duration() - 1, pCore->getCurrentFps()));
2919 }
2920 
2921 void MainWindow::slotLockGuides(bool lock)
2922 {
2923     KdenliveSettings::setLockedGuides(lock);
2924     Q_EMIT getCurrentTimeline()->controller()->guidesLockedChanged();
2925 }
2926 
2927 void MainWindow::slotDeleteGuide()
2928 {
2929     getCurrentTimeline()->controller()->switchGuide(-1, true);
2930 }
2931 
2932 void MainWindow::slotDeleteAllGuides()
2933 {
2934     pCore->currentDoc()->getGuideModel(getCurrentTimeline()->getUuid())->removeAllMarkers();
2935 }
2936 
2937 void MainWindow::slotCutTimelineClip()
2938 {
2939     getCurrentTimeline()->controller()->cutClipUnderCursor();
2940 }
2941 
2942 void MainWindow::slotCutTimelineAllClips()
2943 {
2944     getCurrentTimeline()->controller()->cutAllClipsUnderCursor();
2945 }
2946 
2947 void MainWindow::slotInsertClipOverwrite()
2948 {
2949     const QString &binId = m_clipMonitor->activeClipId();
2950     if (binId.isEmpty()) {
2951         // No clip in monitor
2952         return;
2953     }
2954     std::function<bool(void)> undo = []() { return true; };
2955     std::function<bool(void)> redo = []() { return true; };
2956     bool res = getCurrentTimeline()->controller()->insertZone(binId, m_clipMonitor->getZoneInfo(), true, undo, redo);
2957     if (res) {
2958         pCore->pushUndo(undo, redo, i18n("Overwrite zone"));
2959     } else {
2960         pCore->displayMessage(i18n("Could not insert zone"), ErrorMessage);
2961         undo();
2962     }
2963 }
2964 
2965 void MainWindow::slotInsertClipInsert()
2966 {
2967     const QString &binId = m_clipMonitor->activeClipId();
2968     if (binId.isEmpty()) {
2969         // No clip in monitor
2970         pCore->displayMessage(i18n("No clip selected in project bin"), ErrorMessage);
2971         return;
2972     }
2973     std::function<bool(void)> undo = []() { return true; };
2974     std::function<bool(void)> redo = []() { return true; };
2975     bool res = getCurrentTimeline()->controller()->insertZone(binId, m_clipMonitor->getZoneInfo(), false, undo, redo);
2976     if (res) {
2977         pCore->pushUndo(undo, redo, i18n("Insert zone"));
2978     } else {
2979         pCore->displayMessage(i18n("Could not insert zone"), ErrorMessage);
2980         undo();
2981     }
2982 }
2983 
2984 void MainWindow::slotExtractZone()
2985 {
2986     getCurrentTimeline()->controller()->extractZone(m_clipMonitor->getZoneInfo());
2987 }
2988 
2989 void MainWindow::slotExtractClip()
2990 {
2991     getCurrentTimeline()->controller()->extract();
2992 }
2993 
2994 void MainWindow::slotSaveZoneToBin()
2995 {
2996     getCurrentTimeline()->controller()->saveZone();
2997 }
2998 
2999 void MainWindow::slotLiftZone()
3000 {
3001     getCurrentTimeline()->controller()->extractZone(m_clipMonitor->getZoneInfo(), true);
3002 }
3003 
3004 void MainWindow::slotPreviewRender()
3005 {
3006     if (pCore->currentDoc()) {
3007         getCurrentTimeline()->controller()->startPreviewRender();
3008     }
3009 }
3010 
3011 void MainWindow::slotStopPreviewRender()
3012 {
3013     if (pCore->currentDoc()) {
3014         getCurrentTimeline()->controller()->stopPreviewRender();
3015     }
3016 }
3017 
3018 void MainWindow::slotDefinePreviewRender()
3019 {
3020     if (pCore->currentDoc()) {
3021         getCurrentTimeline()->controller()->addPreviewRange(true);
3022     }
3023 }
3024 
3025 void MainWindow::slotRemovePreviewRender()
3026 {
3027     if (pCore->currentDoc()) {
3028         getCurrentTimeline()->controller()->addPreviewRange(false);
3029     }
3030 }
3031 
3032 void MainWindow::slotClearPreviewRender(bool resetZones)
3033 {
3034     if (pCore->currentDoc()) {
3035         getCurrentTimeline()->controller()->clearPreviewRange(resetZones);
3036     }
3037 }
3038 
3039 void MainWindow::slotSelectTimelineClip()
3040 {
3041     getCurrentTimeline()->controller()->selectCurrentItem(KdenliveObjectType::TimelineClip, true);
3042 }
3043 
3044 void MainWindow::slotSelectTimelineZone()
3045 {
3046     getCurrentTimeline()->controller()->setZoneToSelection();
3047 }
3048 void MainWindow::slotSelectTimelineTransition()
3049 {
3050     bool res = getCurrentTimeline()->controller()->selectCurrentItem(KdenliveObjectType::TimelineComposition, true, false, false);
3051     if (!res) {
3052         getCurrentTimeline()->controller()->selectCurrentItem(KdenliveObjectType::TimelineMix, true);
3053     }
3054 }
3055 
3056 void MainWindow::slotDeselectTimelineClip()
3057 {
3058     getCurrentTimeline()->controller()->selectCurrentItem(KdenliveObjectType::TimelineClip, false);
3059 }
3060 
3061 void MainWindow::slotDeselectTimelineTransition()
3062 {
3063     bool res = getCurrentTimeline()->controller()->selectCurrentItem(KdenliveObjectType::TimelineComposition, false, false, false);
3064     if (!res) {
3065         getCurrentTimeline()->controller()->selectCurrentItem(KdenliveObjectType::TimelineMix, false);
3066     }
3067 }
3068 
3069 void MainWindow::slotSelectAddTimelineClip()
3070 {
3071     getCurrentTimeline()->controller()->selectCurrentItem(KdenliveObjectType::TimelineClip, true, true);
3072 }
3073 
3074 void MainWindow::slotSelectAddTimelineTransition()
3075 {
3076     getCurrentTimeline()->controller()->selectCurrentItem(KdenliveObjectType::TimelineComposition, true, true);
3077 }
3078 
3079 void MainWindow::slotGroupClips()
3080 {
3081     getCurrentTimeline()->controller()->groupSelection();
3082 }
3083 
3084 void MainWindow::slotUnGroupClips()
3085 {
3086     getCurrentTimeline()->controller()->unGroupSelection();
3087 }
3088 
3089 void MainWindow::slotEditItemDuration()
3090 {
3091     getCurrentTimeline()->controller()->editItemDuration();
3092 }
3093 
3094 void MainWindow::slotAddProjectClip(const QUrl &url, const QString &folderInfo)
3095 {
3096     pCore->bin()->droppedUrls(QList<QUrl>() << url, folderInfo);
3097 }
3098 
3099 void MainWindow::slotAddTextNote(const QString &text)
3100 {
3101     pCore->projectManager()->slotAddTextNote(text);
3102 }
3103 
3104 void MainWindow::slotAddProjectClipList(const QList<QUrl> &urls)
3105 {
3106     pCore->bin()->droppedUrls(urls);
3107 }
3108 
3109 void MainWindow::slotAddTransition(QAction *result)
3110 {
3111     if (!result) {
3112         return;
3113     }
3114     // TODO refac
3115     /*
3116     QStringList info = result->data().toStringList();
3117     if (info.isEmpty() || info.count() < 2) {
3118         return;
3119     }
3120     QDomElement transition = transitions.getEffectByTag(info.at(0), info.at(1));
3121     if (pCore->projectManager()->currentTimeline() && !transition.isNull()) {
3122         pCore->projectManager()->currentTimeline()->projectView()->slotAddTransitionToSelectedClips(transition.cloneNode().toElement());
3123     }
3124     */
3125 }
3126 
3127 void MainWindow::slotAddEffect(QAction *result)
3128 {
3129     if (!result) {
3130         return;
3131     }
3132     QString effectId = result->data().toString();
3133     addEffect(effectId);
3134 }
3135 
3136 void MainWindow::addEffect(const QString &effectId)
3137 {
3138     if (m_assetPanel->effectStackOwner().type == KdenliveObjectType::BinClip) {
3139         // Pass the command to bin
3140         pCore->bin()->slotAddEffect({}, {effectId});
3141     } else if (m_assetPanel->effectStackOwner().type == KdenliveObjectType::TimelineTrack ||
3142                m_assetPanel->effectStackOwner().type == KdenliveObjectType::Master) {
3143         if (!m_assetPanel->addEffect(effectId)) {
3144             pCore->displayMessage(i18n("Cannot add effect to active item"), ErrorMessage);
3145         }
3146     } else {
3147         // Add effect to the current timeline selection
3148         QVariantMap effectData;
3149         effectData.insert(QStringLiteral("kdenlive/effect"), effectId);
3150         getCurrentTimeline()->controller()->addAsset(effectData);
3151     }
3152 }
3153 
3154 void MainWindow::slotZoomIn(bool zoomOnMouse)
3155 {
3156     slotSetZoom(m_zoomSlider->value() - 1, zoomOnMouse);
3157     slotShowZoomSliderToolTip();
3158 }
3159 
3160 void MainWindow::slotZoomOut(bool zoomOnMouse)
3161 {
3162     slotSetZoom(m_zoomSlider->value() + 1, zoomOnMouse);
3163     slotShowZoomSliderToolTip();
3164 }
3165 
3166 void MainWindow::slotFitZoom()
3167 {
3168     Q_EMIT m_timelineTabs->fitZoom();
3169 }
3170 
3171 void MainWindow::slotSetZoom(int value, bool zoomOnMouse)
3172 {
3173     value = qBound(m_zoomSlider->minimum(), value, m_zoomSlider->maximum());
3174     Q_EMIT m_timelineTabs->changeZoom(value, zoomOnMouse);
3175     updateZoomSlider(value);
3176 }
3177 
3178 void MainWindow::updateZoomSlider(int value)
3179 {
3180     slotUpdateZoomSliderToolTip(value);
3181     KdenliveDoc *project = pCore->currentDoc();
3182     if (project) {
3183         project->setZoom(pCore->currentTimelineId(), value);
3184     }
3185     m_zoomOut->setEnabled(value < m_zoomSlider->maximum());
3186     m_zoomIn->setEnabled(value > m_zoomSlider->minimum());
3187     QSignalBlocker blocker(m_zoomSlider);
3188     m_zoomSlider->setValue(value);
3189 }
3190 
3191 void MainWindow::slotShowZoomSliderToolTip(int zoomlevel)
3192 {
3193     if (zoomlevel != -1) {
3194         slotUpdateZoomSliderToolTip(zoomlevel);
3195     }
3196 
3197     QPoint global = m_zoomSlider->rect().topLeft();
3198     global.ry() += m_zoomSlider->height() / 2;
3199     QHelpEvent toolTipEvent(QEvent::ToolTip, QPoint(0, 0), m_zoomSlider->mapToGlobal(global));
3200     QApplication::sendEvent(m_zoomSlider, &toolTipEvent);
3201 }
3202 
3203 void MainWindow::slotUpdateZoomSliderToolTip(int zoomlevel)
3204 {
3205     int max = m_zoomSlider->maximum() + 1;
3206     m_zoomSlider->setToolTip(i18n("Zoom Level: %1/%2", max - zoomlevel, max));
3207 }
3208 
3209 void MainWindow::customEvent(QEvent *e)
3210 {
3211     if (e->type() == QEvent::User) {
3212         m_messageLabel->setMessage(static_cast<MltErrorEvent *>(e)->message(), MltError);
3213     }
3214 }
3215 
3216 void MainWindow::slotSnapRewind()
3217 {
3218     if (m_projectMonitor->isActive()) {
3219         getCurrentTimeline()->controller()->gotoPreviousSnap();
3220     } else {
3221         m_clipMonitor->slotSeekToPreviousSnap();
3222     }
3223 }
3224 
3225 void MainWindow::slotSnapForward()
3226 {
3227     if (m_projectMonitor->isActive()) {
3228         getCurrentTimeline()->controller()->gotoNextSnap();
3229     } else {
3230         m_clipMonitor->slotSeekToNextSnap();
3231     }
3232 }
3233 
3234 void MainWindow::slotGuideRewind()
3235 {
3236     if (m_projectMonitor->isActive()) {
3237         getCurrentTimeline()->controller()->gotoPreviousGuide();
3238     } else {
3239         m_clipMonitor->slotSeekToPreviousSnap();
3240     }
3241 }
3242 
3243 void MainWindow::slotGuideForward()
3244 {
3245     if (m_projectMonitor->isActive()) {
3246         getCurrentTimeline()->controller()->gotoNextGuide();
3247     } else {
3248         m_clipMonitor->slotSeekToNextSnap();
3249     }
3250 }
3251 
3252 void MainWindow::slotClipStart()
3253 {
3254     if (m_projectMonitor->isActive()) {
3255         getCurrentTimeline()->controller()->seekCurrentClip(false);
3256     } else {
3257         m_clipMonitor->slotStart();
3258     }
3259 }
3260 
3261 void MainWindow::slotClipEnd()
3262 {
3263     if (m_projectMonitor->isActive()) {
3264         getCurrentTimeline()->controller()->seekCurrentClip(true);
3265     } else {
3266         m_clipMonitor->slotEnd();
3267     }
3268 }
3269 
3270 void MainWindow::slotChangeTool(QAction *action)
3271 {
3272     ToolType::ProjectTool activeTool = ToolType::SelectTool;
3273 
3274     // if(action == m_buttonSelectTool) covered by default value
3275     if (action == m_buttonRazorTool) {
3276         activeTool = ToolType::RazorTool;
3277     } else if (action == m_buttonSpacerTool) {
3278         activeTool = ToolType::SpacerTool;
3279     }
3280     if (action == m_buttonRippleTool) {
3281         activeTool = ToolType::RippleTool;
3282     }
3283     if (action == m_buttonRollTool) {
3284         activeTool = ToolType::RollTool;
3285     }
3286     if (action == m_buttonSlipTool) {
3287         activeTool = ToolType::SlipTool;
3288     }
3289     if (action == m_buttonSlideTool) {
3290         activeTool = ToolType::SlideTool;
3291     }
3292     if (action == m_buttonMulticamTool) {
3293         activeTool = ToolType::MulticamTool;
3294     };
3295     slotSetTool(activeTool);
3296 }
3297 
3298 void MainWindow::slotChangeEdit(QAction *action)
3299 {
3300     TimelineMode::EditMode mode = TimelineMode::NormalEdit;
3301     if (action == m_overwriteEditTool) {
3302         mode = TimelineMode::OverwriteEdit;
3303     } else if (action == m_insertEditTool) {
3304         mode = TimelineMode::InsertEdit;
3305     }
3306     getCurrentTimeline()->model()->setEditMode(mode);
3307     showToolMessage();
3308     if (mode == TimelineMode::InsertEdit) {
3309         // Disable spacer tool in insert mode
3310         if (m_buttonSpacerTool->isChecked()) {
3311             m_buttonSelectTool->setChecked(true);
3312             slotSetTool(ToolType::SelectTool);
3313         }
3314         m_buttonSpacerTool->setEnabled(false);
3315     } else {
3316         m_buttonSpacerTool->setEnabled(true);
3317     }
3318 }
3319 
3320 void MainWindow::disableMulticam()
3321 {
3322     if (m_activeTool == ToolType::MulticamTool) {
3323         m_buttonSelectTool->setChecked(true);
3324         slotSetTool(ToolType::SelectTool);
3325     }
3326 }
3327 
3328 void MainWindow::slotSetTool(ToolType::ProjectTool tool)
3329 {
3330     if (m_activeTool == ToolType::MulticamTool) {
3331         // End multicam operation
3332         pCore->monitorManager()->switchMultiTrackView(false);
3333         pCore->monitorManager()->slotStopMultiTrackMode();
3334     }
3335     m_activeTool = tool;
3336     if (pCore->currentDoc()) {
3337         showToolMessage();
3338         getCurrentTimeline()->setTool(tool);
3339         getCurrentTimeline()->controller()->updateTrimmingMode();
3340     }
3341     if (m_activeTool == ToolType::MulticamTool) {
3342         // Start multicam operation
3343         pCore->monitorManager()->switchMultiTrackView(true);
3344         pCore->monitorManager()->slotStartMultiTrackMode();
3345     }
3346 }
3347 
3348 void MainWindow::showToolMessage()
3349 {
3350     QString message;
3351     QString toolLabel;
3352     if (m_buttonSelectTool->isChecked()) {
3353 #ifdef Q_OS_WIN
3354         message = xi18nc("@info:whatsthis",
3355                          "<shortcut>Shift drag</shortcut> for rubber-band selection, <shortcut>Shift click</shortcut> for multiple "
3356                          "selection, <shortcut>Meta drag</shortcut> to move a grouped clip to another track, <shortcut>Ctrl drag</shortcut> to pan");
3357 #else
3358         message = xi18nc("@info:whatsthis",
3359                          "<shortcut>Shift drag</shortcut> for rubber-band selection, <shortcut>Shift click</shortcut> for multiple "
3360                          "selection, <shortcut>Meta + Alt drag</shortcut> to move a grouped clip to another track, <shortcut>Ctrl drag</shortcut> to pan");
3361 #endif
3362         toolLabel = i18n("Select");
3363     } else if (m_buttonRazorTool->isChecked()) {
3364         message = xi18nc("@info:whatsthis", "<shortcut>Shift</shortcut> to preview cut frame");
3365         toolLabel = i18n("Razor");
3366     } else if (m_buttonSpacerTool->isChecked()) {
3367         message =
3368             xi18nc("@info:whatsthis",
3369                    "<shortcut>Ctrl</shortcut> to apply on current track only, <shortcut>Shift</shortcut> to also move guides. You can combine both modifiers.");
3370         toolLabel = i18n("Spacer");
3371     } else if (m_buttonSlipTool->isChecked()) {
3372         message = xi18nc("@info:whatsthis", "<shortcut>Click</shortcut> on an item to slip, <shortcut>Shift click</shortcut> for multiple selection");
3373         toolLabel = i18nc("Timeline Tool", "Slip");
3374     } /*else if (m_buttonSlideTool->isChecked()) { // TODO implement Slide
3375         toolLabel = i18nc("Timeline Tool", "Slide");
3376     }*/
3377     else if (m_buttonRippleTool->isChecked()) {
3378         message = xi18nc("@info:whatsthis", "<shortcut>Shift drag</shortcut> for rubber-band selection, <shortcut>Shift click</shortcut> for multiple "
3379                                             "selection, <shortcut>Alt click</shortcut> to select an item in a group, <shortcut>Ctrl drag</shortcut> to pan");
3380         toolLabel = i18nc("Timeline Tool", "Ripple");
3381     } /*else if (m_buttonRollTool->isChecked()) { // TODO implement Slide
3382         toolLabel = i18nc("Timeline Tool", "Roll");
3383     }*/
3384     else if (m_buttonMulticamTool->isChecked()) {
3385         message =
3386             xi18nc("@info:whatsthis", "<shortcut>Click</shortcut> on a track view in the project monitor to perform a lift of all tracks except active one");
3387         toolLabel = i18n("Multicam");
3388     }
3389     TimelineMode::EditMode mode = TimelineMode::NormalEdit;
3390     if (getCurrentTimeline() && getCurrentTimeline()->model()) {
3391         mode = getCurrentTimeline()->model()->editMode();
3392     }
3393     if (mode != TimelineMode::NormalEdit) {
3394         if (!toolLabel.isEmpty()) {
3395             toolLabel.append(QStringLiteral(" | "));
3396         }
3397         if (mode == TimelineMode::InsertEdit) {
3398             toolLabel.append(i18n("Insert"));
3399             m_trimLabel->setStyleSheet(QStringLiteral("QLabel { padding-left: 2; padding-right: 2; background-color :red; }"));
3400         } else if (mode == TimelineMode::OverwriteEdit) {
3401             toolLabel.append(i18n("Overwrite"));
3402             m_trimLabel->setStyleSheet(QStringLiteral("QLabel { padding-left: 2; padding-right: 2; background-color :darkGreen; }"));
3403         }
3404     } else {
3405         m_trimLabel->setStyleSheet(
3406             QStringLiteral("QLabel { padding-left: 2; padding-right: 2; background-color :%1; }").arg(palette().window().color().name()));
3407     }
3408     m_trimLabel->setText(toolLabel);
3409     m_messageLabel->setKeyMap(message);
3410 }
3411 
3412 void MainWindow::setWidgetKeyBinding(const QString &mess)
3413 {
3414     m_messageLabel->setKeyMap(mess);
3415 }
3416 
3417 void MainWindow::showKeyBinding(const QString &text)
3418 {
3419     m_messageLabel->setTmpKeyMap(text);
3420 }
3421 
3422 void MainWindow::slotCopy()
3423 {
3424     QWidget *widget = QApplication::focusWidget();
3425     while ((widget != nullptr) && widget != this) {
3426         if (widget == m_effectStackDock) {
3427             m_assetPanel->sendStandardCommand(KStandardAction::Copy);
3428             return;
3429         }
3430         widget = widget->parentWidget();
3431     }
3432     getCurrentTimeline()->controller()->copyItem();
3433 }
3434 
3435 void MainWindow::slotPaste()
3436 {
3437     QWidget *widget = QApplication::focusWidget();
3438     while ((widget != nullptr) && widget != this) {
3439         if (widget == m_effectStackDock) {
3440             m_assetPanel->sendStandardCommand(KStandardAction::Paste);
3441             return;
3442         }
3443         widget = widget->parentWidget();
3444     }
3445     getCurrentTimeline()->controller()->pasteItem();
3446 }
3447 
3448 void MainWindow::slotPasteEffects()
3449 {
3450     getCurrentTimeline()->controller()->pasteEffects();
3451 }
3452 
3453 void MainWindow::slotClipInTimeline(const QString &clipId, const QList<int> &ids)
3454 {
3455     Q_UNUSED(clipId)
3456     QMenu *inTimelineMenu = static_cast<QMenu *>(factory()->container(QStringLiteral("clip_in_timeline"), this));
3457     QList<QAction *> actionList;
3458     for (int i = 0; i < ids.count(); ++i) {
3459         ObjectId oid(KdenliveObjectType::TimelineClip, ids.at(i), pCore->currentTimelineId());
3460         QString track = getCurrentTimeline()->controller()->getTrackNameFromIndex(pCore->getItemTrack(oid));
3461         QString start = pCore->currentDoc()->timecode().getTimecodeFromFrames(pCore->getItemPosition(oid));
3462         int j = 0;
3463         QAction *a = new QAction(track + QStringLiteral(": ") + start, inTimelineMenu);
3464         a->setData(ids.at(i));
3465         connect(a, &QAction::triggered, this, &MainWindow::slotSelectClipInTimeline);
3466         while (j < actionList.count()) {
3467             if (actionList.at(j)->text() > a->text()) {
3468                 break;
3469             }
3470             j++;
3471         }
3472         actionList.insert(j, a);
3473     }
3474     QList<QAction *> list = inTimelineMenu->actions();
3475     unplugActionList(QStringLiteral("timeline_occurences"));
3476     qDeleteAll(list);
3477     plugActionList(QStringLiteral("timeline_occurences"), actionList);
3478 
3479     if (actionList.isEmpty()) {
3480         inTimelineMenu->setEnabled(false);
3481     } else {
3482         inTimelineMenu->setEnabled(true);
3483     }
3484 }
3485 
3486 void MainWindow::raiseBin()
3487 {
3488     Bin *bin = activeBin();
3489     if (bin) {
3490         bin->parentWidget()->setVisible(true);
3491         bin->parentWidget()->raise();
3492     }
3493 }
3494 
3495 void MainWindow::slotClipInProjectTree()
3496 {
3497     QList<int> ids = getCurrentTimeline()->controller()->selection();
3498     if (!ids.isEmpty()) {
3499         const QString binId = getCurrentTimeline()->controller()->getClipBinId(ids.constFirst());
3500         // If we have multiple bins, check first if a visible bin contains it
3501         bool binFound = false;
3502         if (binCount() > 1) {
3503             for (auto &bin : m_binWidgets) {
3504                 if (bin->isVisible() && !bin->visibleRegion().isEmpty()) {
3505                     // Check if clip is a child of this bin
3506                     if (bin->containsId(binId)) {
3507                         binFound = true;
3508                         bin->setFocus();
3509                         raiseBin();
3510                     }
3511                 }
3512             }
3513         }
3514         if (!binFound) {
3515             raiseBin();
3516         }
3517         ObjectId id(KdenliveObjectType::TimelineClip, ids.constFirst(), pCore->currentTimelineId());
3518         int start = pCore->getItemIn(id);
3519         int duration = pCore->getItemDuration(id);
3520         int pos = m_projectMonitor->position();
3521         int itemPos = pCore->getItemPosition(id);
3522         bool containsPos = (pos >= itemPos && pos < itemPos + duration);
3523         double speed = pCore->getClipSpeed(id.itemId);
3524         if (containsPos) {
3525             pos -= itemPos - start;
3526         }
3527         if (!qFuzzyCompare(speed, 1.)) {
3528             if (speed > 0.) {
3529                 // clip has a speed effect, adjust zone
3530                 start = qRound(start * speed);
3531                 duration = qRound(duration * speed);
3532                 if (containsPos) {
3533                     pos = qRound(pos * speed);
3534                 }
3535             } else if (speed < 0.) {
3536                 int max = getCurrentTimeline()->controller()->clipMaxDuration(id.itemId);
3537                 if (max > 0) {
3538                     int invertedPos = itemPos + duration - m_projectMonitor->position();
3539                     start = qRound((max - (start + duration)) * -speed);
3540                     duration = qRound(duration * -speed);
3541                     if (containsPos) {
3542                         pos = start + qRound(invertedPos * -speed);
3543                     }
3544                 }
3545             }
3546         }
3547         QPoint zone(start, start + duration - 1);
3548         if (!containsPos) {
3549             pos = start;
3550         }
3551         activeBin()->selectClipById(binId, pos, zone, true);
3552     }
3553 }
3554 
3555 void MainWindow::slotSelectClipInTimeline()
3556 {
3557     pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
3558     auto *action = qobject_cast<QAction *>(sender());
3559     int clipId = action->data().toInt();
3560     getCurrentTimeline()->controller()->focusItem(clipId);
3561 }
3562 
3563 /** Gets called when the window gets hidden */
3564 void MainWindow::hideEvent(QHideEvent * /*event*/)
3565 {
3566     if (isMinimized() && pCore->monitorManager()) {
3567         pCore->monitorManager()->pauseActiveMonitor();
3568     }
3569 }
3570 
3571 void MainWindow::slotResizeItemStart()
3572 {
3573     getCurrentTimeline()->controller()->setInPoint(m_activeTool == ToolType::RippleTool);
3574 }
3575 
3576 void MainWindow::slotResizeItemEnd()
3577 {
3578     getCurrentTimeline()->controller()->setOutPoint(m_activeTool == ToolType::RippleTool);
3579 }
3580 
3581 #if KXMLGUI_VERSION < QT_VERSION_CHECK(5, 98, 0)
3582 int MainWindow::getNewStuff(const QString &configFile)
3583 {
3584 #if KNEWSTUFF_VERSION > QT_VERSION_CHECK(5, 240, 0)
3585     KNSWidgets::Dialog dialog(configFile);
3586 #else
3587     KNS3::QtQuickDialogWrapper dialog(configFile);
3588 #endif
3589     const QList<KNSCore::EntryInternal> entries = dialog.exec();
3590     for (const auto &entry : qAsConst(entries)) {
3591         if (entry.status() == KNS3::Entry::Installed) {
3592             qCDebug(KDENLIVE_LOG) << "// Installed files: " << entry.installedFiles();
3593         }
3594     }
3595     return entries.size();
3596 }
3597 #endif
3598 
3599 #if KXMLGUI_VERSION < QT_VERSION_CHECK(5, 98, 0)
3600 void MainWindow::slotGetNewKeyboardStuff(QComboBox *schemesList)
3601 {
3602     if (getNewStuff(QStringLiteral(":data/kdenlive_keyboardschemes.knsrc")) > 0) {
3603         // Refresh keyboard schemes list (schemes list creation code copied from KShortcutSchemesEditor)
3604         QStringList schemes;
3605         schemes << QStringLiteral("Default");
3606         // List files in the shortcuts subdir, each one is a scheme. See KShortcutSchemesHelper::{shortcutSchemeFileName,exportActionCollection}
3607         const QStringList shortcutsDirs = QStandardPaths::locateAll(
3608             QStandardPaths::GenericDataLocation, QCoreApplication::applicationName() + QStringLiteral("/shortcuts"), QStandardPaths::LocateDirectory);
3609         qCDebug(KDENLIVE_LOG) << "shortcut scheme dirs:" << shortcutsDirs;
3610         for (const QString &dir : shortcutsDirs) {
3611             for (const QString &file : QDir(dir).entryList(QDir::Files | QDir::NoDotAndDotDot)) {
3612                 qCDebug(KDENLIVE_LOG) << "shortcut scheme file:" << file;
3613                 schemes << file;
3614             }
3615         }
3616         schemesList->clear();
3617         schemesList->addItems(schemes);
3618     }
3619 }
3620 #endif
3621 
3622 void MainWindow::slotAutoTransition()
3623 {
3624     // TODO refac
3625     /*
3626     if (pCore->projectManager()->currentTimeline()) {
3627         pCore->projectManager()->currentTimeline()->projectView()->autoTransition();
3628     }
3629     */
3630 }
3631 
3632 void MainWindow::slotSplitAV()
3633 {
3634     getCurrentTimeline()->controller()->splitAV();
3635 }
3636 
3637 void MainWindow::slotSwitchClip()
3638 {
3639     getCurrentTimeline()->controller()->switchEnableState();
3640 }
3641 
3642 void MainWindow::slotSetAudioAlignReference()
3643 {
3644     getCurrentTimeline()->controller()->setAudioRef();
3645 }
3646 
3647 void MainWindow::slotAlignAudio()
3648 {
3649     getCurrentTimeline()->controller()->alignAudio();
3650 }
3651 
3652 void MainWindow::slotUpdateTimelineView(QAction *action)
3653 {
3654     int viewMode = action->data().toInt();
3655     KdenliveSettings::setAudiotracksbelow(viewMode);
3656     getCurrentTimeline()->model()->_resetView();
3657 }
3658 
3659 void MainWindow::slotShowTimeline(bool show)
3660 {
3661     if (!show) {
3662         m_timelineState = saveState();
3663         centralWidget()->setHidden(true);
3664     } else {
3665         centralWidget()->setHidden(false);
3666         restoreState(m_timelineState);
3667     }
3668 }
3669 
3670 void MainWindow::loadClipActions()
3671 {
3672     unplugActionList(QStringLiteral("add_effect"));
3673     plugActionList(QStringLiteral("add_effect"), m_effectsMenu->actions());
3674 
3675     QList<QAction *> clipJobActions = getExtraActions(QStringLiteral("clipjobs"));
3676     unplugActionList(QStringLiteral("clip_jobs"));
3677     plugActionList(QStringLiteral("clip_jobs"), clipJobActions);
3678 
3679     QList<QAction *> atcActions = getExtraActions(QStringLiteral("audiotranscoderslist"));
3680     unplugActionList(QStringLiteral("audio_transcoders_list"));
3681     plugActionList(QStringLiteral("audio_transcoders_list"), atcActions);
3682 
3683     QList<QAction *> tcActions = getExtraActions(QStringLiteral("transcoderslist"));
3684     unplugActionList(QStringLiteral("transcoders_list"));
3685     plugActionList(QStringLiteral("transcoders_list"), tcActions);
3686 }
3687 
3688 void MainWindow::loadDockActions()
3689 {
3690     QList<QAction *> list = kdenliveCategoryMap.value(QStringLiteral("interface"))->actions();
3691     // Sort actions
3692     QMap<QString, QAction *> sorted;
3693     QStringList sortedList;
3694     for (QAction *a : qAsConst(list)) {
3695         if (a->objectName().startsWith(QStringLiteral("raise_"))) {
3696             continue;
3697         }
3698         sorted.insert(a->text(), a);
3699         sortedList << a->text();
3700     }
3701     QList<QAction *> orderedList;
3702     sortedList.sort(Qt::CaseInsensitive);
3703     for (const QString &text : qAsConst(sortedList)) {
3704         orderedList << sorted.value(text);
3705     }
3706     unplugActionList(QStringLiteral("dock_actions"));
3707     plugActionList(QStringLiteral("dock_actions"), orderedList);
3708 }
3709 
3710 void MainWindow::buildDynamicActions()
3711 {
3712     KActionCategory *ts = nullptr;
3713     if (kdenliveCategoryMap.contains(QStringLiteral("clipjobs"))) {
3714         ts = kdenliveCategoryMap.take(QStringLiteral("clipjobs"));
3715         delete ts;
3716     }
3717     ts = new KActionCategory(i18n("Clip Jobs"), m_extraFactory->actionCollection());
3718 
3719     QAction *action;
3720     QMap<QString, QString> jobValues = ClipJobManager::getClipJobNames();
3721     QMapIterator<QString, QString> k(jobValues);
3722     while (k.hasNext()) {
3723         k.next();
3724         action = new QAction(k.value(), m_extraFactory->actionCollection());
3725         action->setData(k.key());
3726         if (k.key() == QLatin1String("stabilize;v")) {
3727             connect(action, &QAction::triggered, this, [this]() { StabilizeTask::start(this); });
3728         } else if (k.key() == QLatin1String("scenesplit;v")) {
3729             connect(action, &QAction::triggered, this, [&]() { SceneSplitTask::start(this); });
3730         } else if (k.key() == QLatin1String("timewarp;av")) {
3731             connect(action, &QAction::triggered, this, [&]() { SpeedTask::start(this); });
3732         } else {
3733             connect(action, &QAction::triggered, this, [&, jobId = k.key().section(QLatin1Char(';'), 0, 0)]() { CustomJobTask::start(this, jobId); });
3734         }
3735         ts->addAction(action->text(), action);
3736     }
3737 
3738     action = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure Clip Jobs…"), m_extraFactory->actionCollection());
3739     ts->addAction(action->text(), action);
3740     connect(action, &QAction::triggered, this, [this]() { manageClipJobs(); });
3741 
3742     kdenliveCategoryMap.insert(QStringLiteral("clipjobs"), ts);
3743 
3744     if (kdenliveCategoryMap.contains(QStringLiteral("transcoderslist"))) {
3745         ts = kdenliveCategoryMap.take(QStringLiteral("transcoderslist"));
3746         delete ts;
3747     }
3748     if (kdenliveCategoryMap.contains(QStringLiteral("audiotranscoderslist"))) {
3749         ts = kdenliveCategoryMap.take(QStringLiteral("audiotranscoderslist"));
3750         delete ts;
3751     }
3752     // transcoders
3753     ts = new KActionCategory(i18n("Transcoders"), m_extraFactory->actionCollection());
3754     KActionCategory *ats = new KActionCategory(i18n("Extract Audio"), m_extraFactory->actionCollection());
3755     KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("kdenlivetranscodingrc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
3756     KConfigGroup transConfig(config, "Transcoding");
3757     // read the entries
3758     QMap<QString, QString> profiles = transConfig.entryMap();
3759     QMapIterator<QString, QString> i(profiles);
3760     while (i.hasNext()) {
3761         i.next();
3762         QStringList transList;
3763         transList << i.value().split(QLatin1Char(';'));
3764         auto *a = new QAction(i.key(), m_extraFactory->actionCollection());
3765         a->setData(transList);
3766         if (transList.count() > 1) {
3767             a->setToolTip(transList.at(1));
3768         }
3769         connect(a, &QAction::triggered, [&, a]() {
3770             QStringList transcodeData = a->data().toStringList();
3771             std::vector<QString> ids = pCore->bin()->selectedClipsIds(true);
3772             QMap<QString, QVector<int>> clipStreamSelection;
3773             QString clipId;
3774             if (transcodeData.count() > 2 && transcodeData.at(2) == QLatin1String("audio")) {
3775                 // Audio extract, check if we have multi stream clips
3776                 QMap<QString, int> clipStreamCount;
3777                 for (const QString &id : ids) {
3778                     if (id.contains(QLatin1Char('/'))) {
3779                         clipId = id.section(QLatin1Char('/'), 0, 0);
3780                     } else {
3781                         clipId = id;
3782                     }
3783                     std::shared_ptr<ProjectClip> clip = pCore->projectItemModel()->getClipByBinID(clipId);
3784                     if (clip->audioStreamsCount() > 1) {
3785                         clipStreamSelection.insert(clipId, clip->activeFfmpegStreams());
3786                     }
3787                 }
3788             }
3789             const QString tData = transcodeData.first();
3790             int clipIn = -1;
3791             int clipOut = -1;
3792             for (const QString &id : ids) {
3793                 if (id.contains(QLatin1Char('/'))) {
3794                     clipId = id.section(QLatin1Char('/'), 0, 0);
3795                     clipIn = id.section(QLatin1Char('/'), 1, 1).toInt();
3796                     clipOut = id.section(QLatin1Char('/'), 2, 2).toInt();
3797                 } else {
3798                     clipId = id;
3799                     clipIn = -1;
3800                     clipOut = -1;
3801                 }
3802                 std::shared_ptr<ProjectClip> clip = pCore->projectItemModel()->getClipByBinID(clipId);
3803                 if (clipStreamSelection.contains(clipId)) {
3804                     // Extract selected audio streams only, create one task per stream
3805                     QVector<int> selectedStreams = clipStreamSelection.value(clipId);
3806                     for (auto &ix : selectedStreams) {
3807                         QString args;
3808                         QString suffix;
3809                         if (ix == -1) {
3810                             // Merge all audio streams
3811                             args = QStringLiteral("-filter_complex amerge=inputs=%1 ").arg(clip->audioStreamsCount());
3812                             args.append(QStringLiteral("-ac %1 ").arg(clip->audioChannels()));
3813                             suffix = i18n("-merged");
3814                         } else {
3815                             args = QStringLiteral("-map 0:a:%1 ").arg(ix);
3816                             suffix = i18n("-stream-%1", ix);
3817                         }
3818                         args.append(tData);
3819                         TranscodeTask::start(ObjectId(KdenliveObjectType::BinClip, clipId.toInt(), QUuid()), suffix, QString(), args, clipIn, clipOut, false,
3820                                              clip.get());
3821                     }
3822                 } else {
3823                     TranscodeTask::start(ObjectId(KdenliveObjectType::BinClip, clipId.toInt(), QUuid()), QString(), QString(), tData, clipIn, clipOut, false,
3824                                          clip.get());
3825                 }
3826             }
3827         });
3828         if (transList.count() > 2 && transList.at(2) == QLatin1String("audio")) {
3829             // This is an audio transcoding action
3830             ats->addAction(i.key(), a);
3831         } else {
3832             ts->addAction(i.key(), a);
3833         }
3834     }
3835     kdenliveCategoryMap.insert(QStringLiteral("transcoderslist"), ts);
3836     kdenliveCategoryMap.insert(QStringLiteral("audiotranscoderslist"), ats);
3837 
3838     updateDockMenu();
3839 }
3840 
3841 void MainWindow::updateDockMenu()
3842 {
3843     // Populate View menu with show / hide actions for dock widgets
3844     KActionCategory *guiActions = nullptr;
3845     if (kdenliveCategoryMap.contains(QStringLiteral("interface"))) {
3846         guiActions = kdenliveCategoryMap.take(QStringLiteral("interface"));
3847         delete guiActions;
3848     }
3849     guiActions = new KActionCategory(i18n("Interface"), actionCollection());
3850     QAction *showTimeline = new QAction(i18n("Timeline"), this);
3851     showTimeline->setCheckable(true);
3852     showTimeline->setChecked(true);
3853     connect(showTimeline, &QAction::triggered, this, &MainWindow::slotShowTimeline);
3854     guiActions->addAction(showTimeline->text(), showTimeline);
3855     actionCollection()->addAction(showTimeline->text(), showTimeline);
3856 
3857     QList<QDockWidget *> docks = findChildren<QDockWidget *>();
3858     for (auto dock : qAsConst(docks)) {
3859         QAction *dockInformations = dock->toggleViewAction();
3860         if (!dockInformations) {
3861             continue;
3862         }
3863         dockInformations->setChecked(!dock->isHidden());
3864         guiActions->addAction(dockInformations->text(), dockInformations);
3865         QAction *action = new QAction(i18n("Raise %1", dockInformations->text()), this);
3866         connect(action, &QAction::triggered, this, [dock]() {
3867             dock->raise();
3868             dock->setFocus();
3869         });
3870         addAction("raise_" + dock->objectName(), action, {}, guiActions);
3871     }
3872     kdenliveCategoryMap.insert(QStringLiteral("interface"), guiActions);
3873 }
3874 
3875 QList<QAction *> MainWindow::getExtraActions(const QString &name)
3876 {
3877     if (!kdenliveCategoryMap.contains(name)) {
3878         return QList<QAction *>();
3879     }
3880     return kdenliveCategoryMap.value(name)->actions();
3881 }
3882 
3883 void MainWindow::slotTranscode(const QStringList &urls)
3884 {
3885     Q_ASSERT(!urls.isEmpty());
3886     QString params;
3887     QString desc;
3888     ClipTranscode *d = new ClipTranscode(urls, params, QStringList(), desc, pCore->bin()->getCurrentFolder());
3889     connect(d, &ClipTranscode::addClip, this, &MainWindow::slotAddProjectClip);
3890     d->show();
3891 }
3892 
3893 void MainWindow::slotFriendlyTranscode(const QString &binId, bool checkProfile)
3894 {
3895     QString params;
3896     QString desc;
3897     std::shared_ptr<ProjectClip> clip = pCore->projectItemModel()->getClipByBinID(binId);
3898     if (clip == nullptr) {
3899         qDebug() << "// NO CLIP FOUND FOR BIN ID: " << binId;
3900         return;
3901     }
3902     QStringList urls = {clip->url()};
3903     // Prepare clip properties
3904     QMap<QString, QString> sourceProps;
3905     sourceProps.insert(QStringLiteral("resource"), clip->url());
3906     sourceProps.insert(QStringLiteral("kdenlive:originalurl"), clip->url());
3907     sourceProps.insert(QStringLiteral("kdenlive:clipname"), clip->clipName());
3908     sourceProps.insert(QStringLiteral("kdenlive:proxy"), clip->getProducerProperty(QStringLiteral("kdenlive:proxy")));
3909     sourceProps.insert(QStringLiteral("_fullreload"), QStringLiteral("1"));
3910     ClipTranscode *d = new ClipTranscode(urls, params, QStringList(), desc, pCore->bin()->getCurrentFolder());
3911     connect(d, &ClipTranscode::addClip, [&, binId, sourceProps](const QUrl &url, const QString & /*folderInfo*/) {
3912         QMap<QString, QString> newProps;
3913         newProps.insert(QStringLiteral("resource"), url.toLocalFile());
3914         newProps.insert(QStringLiteral("kdenlive:originalurl"), url.toLocalFile());
3915         newProps.insert(QStringLiteral("kdenlive:clipname"), url.fileName());
3916         newProps.insert(QStringLiteral("kdenlive:proxy"), QStringLiteral("-"));
3917         newProps.insert(QStringLiteral("_fullreload"), QStringLiteral("1"));
3918         QMetaObject::invokeMethod(pCore->bin(), "slotEditClipCommand", Qt::QueuedConnection, Q_ARG(QString, binId), Q_ARG(stringMap, sourceProps),
3919                                   Q_ARG(stringMap, newProps));
3920     });
3921     d->exec();
3922     if (checkProfile) {
3923         pCore->bin()->slotCheckProfile(binId);
3924     }
3925 }
3926 
3927 void MainWindow::slotTranscodeClip()
3928 {
3929     const QString dialogFilter = ClipCreationDialog::getExtensionsFilter(QStringList() << i18n("All Files") + QStringLiteral(" (*)"));
3930     QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder"));
3931     QStringList urls = QFileDialog::getOpenFileNames(this, i18nc("@title:window", "Files to Transcode"), clipFolder, dialogFilter);
3932     if (urls.isEmpty()) {
3933         return;
3934     }
3935     slotTranscode(urls);
3936 }
3937 
3938 void MainWindow::slotSetDocumentRenderProfile(const QMap<QString, QString> &props)
3939 {
3940     KdenliveDoc *project = pCore->currentDoc();
3941     bool modified = false;
3942     QMapIterator<QString, QString> i(props);
3943     while (i.hasNext()) {
3944         i.next();
3945         if (project->getDocumentProperty(i.key()) == i.value()) {
3946             continue;
3947         }
3948         project->setDocumentProperty(i.key(), i.value());
3949         modified = true;
3950     }
3951     if (modified) {
3952         project->setModified();
3953     }
3954 }
3955 
3956 void MainWindow::slotUpdateTimecodeFormat(int ix)
3957 {
3958     KdenliveSettings::setFrametimecode(ix == 1);
3959     Q_EMIT pCore->updateProjectTimecode();
3960     m_clipMonitor->updateTimecodeFormat();
3961     m_projectMonitor->updateTimecodeFormat();
3962     Q_EMIT getCurrentTimeline()->controller()->frameFormatChanged();
3963     m_timeFormatButton->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
3964 }
3965 
3966 void MainWindow::slotRemoveFocus()
3967 {
3968     getCurrentTimeline()->setFocus();
3969 }
3970 
3971 void MainWindow::slotShutdown()
3972 {
3973     pCore->currentDoc()->setModified(false);
3974     // Call shutdown
3975 #ifndef NODBUS
3976     QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface();
3977     // org.kde.Shutdown is DBus activatable, so we can't query for it running
3978     if (qgetenv("XDG_CURRENT_DESKTOP") == QLatin1String("KDE")) {
3979         QDBusInterface kdeShutdown(QStringLiteral("org.kde.Shutdown"), QStringLiteral("/Shutdown"), QStringLiteral("org.kde.Shutdown"));
3980         kdeShutdown.call(QStringLiteral("logoutAndShutdown"));
3981     } else if ((interface != nullptr) && interface->isServiceRegistered(QStringLiteral("org.gnome.SessionManager"))) {
3982         QDBusInterface smserver(QStringLiteral("org.gnome.SessionManager"), QStringLiteral("/org/gnome/SessionManager"),
3983                                 QStringLiteral("org.gnome.SessionManager"));
3984         smserver.call(QStringLiteral("Shutdown"));
3985     }
3986 #endif
3987 }
3988 
3989 void MainWindow::slotSwitchMonitors()
3990 {
3991     pCore->monitorManager()->slotSwitchMonitors(!m_clipMonitor->isActive());
3992     if (m_projectMonitor->isActive()) {
3993         getCurrentTimeline()->setFocus();
3994     } else {
3995         pCore->bin()->focusBinView();
3996     }
3997 }
3998 
3999 void MainWindow::slotFocusTimecode()
4000 {
4001     if (m_clipMonitor->isActive()) {
4002         m_clipMonitor->focusTimecode();
4003     } else if (m_projectMonitor) {
4004         m_projectMonitor->focusTimecode();
4005     }
4006 }
4007 
4008 void MainWindow::slotSwitchMonitorOverlay(QAction *action)
4009 {
4010     if (pCore->monitorManager()->isActive(Kdenlive::ClipMonitor)) {
4011         m_clipMonitor->switchMonitorInfo(action->data().toInt());
4012     } else {
4013         m_projectMonitor->switchMonitorInfo(action->data().toInt());
4014     }
4015 }
4016 
4017 void MainWindow::slotSwitchDropFrames(bool drop)
4018 {
4019     KdenliveSettings::setMonitor_dropframes(drop);
4020     m_clipMonitor->restart();
4021     m_projectMonitor->restart();
4022 }
4023 
4024 void MainWindow::slotSetMonitorGamma(int gamma)
4025 {
4026     KdenliveSettings::setMonitor_gamma(gamma);
4027     m_clipMonitor->restart();
4028     m_projectMonitor->restart();
4029 }
4030 
4031 void MainWindow::slotInsertZoneToTree()
4032 {
4033     if (!m_clipMonitor->isActive() || m_clipMonitor->currentController() == nullptr) {
4034         return;
4035     }
4036     QPoint info = m_clipMonitor->getZoneInfo();
4037     QString id;
4038     // clip monitor counts the frame after the out point as the zone out, so we
4039     // need to subtract 1 to get the actual last frame
4040     pCore->projectItemModel()->requestAddBinSubClip(id, info.x(), info.y()-1, {}, m_clipMonitor->activeClipId());
4041 }
4042 
4043 void MainWindow::slotMonitorRequestRenderFrame(bool request)
4044 {
4045     if (request) {
4046         m_projectMonitor->sendFrameForAnalysis(true);
4047         return;
4048     }
4049     for (int i = 0; i < m_gfxScopesList.count(); ++i) {
4050         if (m_gfxScopesList.at(i)->isVisible() && tabifiedDockWidgets(m_gfxScopesList.at(i)).isEmpty() &&
4051             static_cast<AbstractGfxScopeWidget *>(m_gfxScopesList.at(i)->widget())->autoRefreshEnabled()) {
4052             request = true;
4053             break;
4054         }
4055     }
4056 
4057 #ifdef DEBUG_MAINW
4058     qCDebug(KDENLIVE_LOG) << "Any scope accepting new frames? " << request;
4059 #endif
4060     if (!request) {
4061         m_projectMonitor->sendFrameForAnalysis(false);
4062     }
4063 }
4064 
4065 void MainWindow::slotUpdateProxySettings()
4066 {
4067     KdenliveDoc *project = pCore->currentDoc();
4068     if (m_renderWidget) {
4069         m_renderWidget->updateProxyConfig(project->useProxy());
4070     }
4071     pCore->bin()->refreshProxySettings();
4072 }
4073 
4074 void MainWindow::slotArchiveProject()
4075 {
4076     KdenliveDoc *doc = pCore->currentDoc();
4077     pCore->projectManager()->prepareSave();
4078     QString sceneData = pCore->projectManager()->projectSceneList(doc->url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile());
4079     if (sceneData.isEmpty()) {
4080         KMessageBox::error(this, i18n("Project file could not be saved for archiving."));
4081         return;
4082     }
4083     QPointer<ArchiveWidget> d(new ArchiveWidget(doc->url().fileName(), sceneData, getCurrentTimeline()->controller()->extractCompositionLumas(),
4084                                                 getCurrentTimeline()->controller()->extractExternalEffectFiles(), this));
4085     if (d->exec() != 0) {
4086         m_messageLabel->setMessage(i18n("Archiving project"), OperationCompletedMessage);
4087     }
4088 }
4089 
4090 void MainWindow::slotDownloadResources()
4091 {
4092     QString currentFolder;
4093     if (pCore->currentDoc()) {
4094         currentFolder = pCore->currentDoc()->projectDataFolder();
4095     } else {
4096         currentFolder = KdenliveSettings::defaultprojectfolder();
4097     }
4098     m_onlineResourcesDock->show();
4099     m_onlineResourcesDock->raise();
4100     ;
4101 }
4102 
4103 void MainWindow::slotProcessImportKeyframes(GraphicsRectItem type, const QString &tag, const QString &keyframes)
4104 {
4105     Q_UNUSED(keyframes)
4106     Q_UNUSED(tag)
4107     if (type == AVWidget) {
4108         // This data should be sent to the effect stack
4109         // TODO REFAC reimplement
4110         // m_effectStack->setKeyframes(tag, data);
4111     } else if (type == TransitionWidget) {
4112         // This data should be sent to the transition stack
4113         // TODO REFAC reimplement
4114         // m_effectStack->transitionConfig()->setKeyframes(tag, data);
4115     } else {
4116         // Error
4117     }
4118 }
4119 
4120 void MainWindow::slotAlignPlayheadToMousePos()
4121 {
4122     pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
4123     getCurrentTimeline()->controller()->seekToMouse();
4124 }
4125 
4126 void MainWindow::triggerKey(QKeyEvent *ev)
4127 {
4128     // Hack: The QQuickWindow that displays fullscreen monitor does not integrate with QActions.
4129     // So on keypress events we parse keys and check for shortcuts in all existing actions
4130     QKeySequence seq;
4131     // Remove the Num modifier or some shortcuts like "*" will not work
4132     if (ev->modifiers() != Qt::KeypadModifier) {
4133         seq = QKeySequence(ev->key() + static_cast<int>(ev->modifiers()));
4134     } else {
4135         seq = QKeySequence(ev->key());
4136     }
4137     QList<KActionCollection *> collections = KActionCollection::allCollections();
4138     for (int i = 0; i < collections.count(); ++i) {
4139         KActionCollection *coll = collections.at(i);
4140         for (QAction *tempAction : coll->actions()) {
4141             if (tempAction->shortcuts().contains(seq)) {
4142                 // Trigger action
4143                 tempAction->trigger();
4144                 ev->accept();
4145                 return;
4146             }
4147         }
4148     }
4149     QWidget::keyPressEvent(ev);
4150 }
4151 
4152 QDockWidget *MainWindow::addDock(const QString &title, const QString &objectName, QWidget *widget, Qt::DockWidgetArea area)
4153 {
4154     QDockWidget *dockWidget = new QDockWidget(title, this);
4155     dockWidget->setObjectName(objectName);
4156     dockWidget->setWidget(widget);
4157     addDockWidget(area, dockWidget);
4158 
4159     // Add action to raise and focus the Dock (e.g. with a shortcut)
4160     /*QAction *action = new QAction(i18n("Raise %1", title), this);
4161     connect(action, &QAction::triggered, this, [dockWidget](){
4162         dockWidget->raise();
4163         dockWidget->setFocus();
4164     });
4165     addAction("raise_" + objectName, action, {});*/
4166     return dockWidget;
4167 }
4168 
4169 bool MainWindow::isMixedTabbed() const
4170 {
4171     return !tabifiedDockWidgets(m_mixerDock).isEmpty();
4172 }
4173 
4174 void MainWindow::slotUpdateMonitorOverlays(int id, int code)
4175 {
4176     QMenu *monitorOverlay = static_cast<QMenu *>(factory()->container(QStringLiteral("monitor_config_overlay"), this));
4177     if (!monitorOverlay) {
4178         return;
4179     }
4180     QList<QAction *> actions = monitorOverlay->actions();
4181     for (QAction *ac : qAsConst(actions)) {
4182         int mid = ac->data().toInt();
4183         if (mid == 0x010 || mid == 0x040) {
4184             ac->setVisible(id == Kdenlive::ClipMonitor);
4185         }
4186         ac->setChecked(code & mid);
4187     }
4188 }
4189 
4190 void MainWindow::slotChangeStyle(QAction *a)
4191 {
4192     QString style = a->data().toString();
4193     KdenliveSettings::setWidgetstyle(style);
4194     doChangeStyle();
4195     // Monitor refresh is necessary
4196     raiseMonitor(pCore->monitorManager()->isActive(Kdenlive::ClipMonitor));
4197 }
4198 
4199 void MainWindow::raiseMonitor(bool clipMonitor)
4200 {
4201     if (clipMonitor) {
4202         m_clipMonitorDock->show();
4203         m_clipMonitorDock->raise();
4204     } else {
4205         m_projectMonitorDock->show();
4206         m_projectMonitorDock->raise();
4207     }
4208 }
4209 
4210 void MainWindow::doChangeStyle()
4211 {
4212     QString newStyle = KdenliveSettings::widgetstyle();
4213     if (newStyle.isEmpty() || newStyle == QStringLiteral("Default")) {
4214         newStyle = defaultStyle("Breeze");
4215     }
4216     QApplication::setStyle(QStyleFactory::create(newStyle));
4217 }
4218 
4219 bool MainWindow::isTabbedWith(QDockWidget *widget, const QString &otherWidget)
4220 {
4221     QList<QDockWidget *> tabbed = tabifiedDockWidgets(widget);
4222     for (auto tab : qAsConst(tabbed)) {
4223         if (tab->objectName() == otherWidget) {
4224             return true;
4225         }
4226     }
4227     return false;
4228 }
4229 
4230 void MainWindow::slotToggleAutoPreview(bool enable)
4231 {
4232     KdenliveSettings::setAutopreview(enable);
4233     if (enable && getCurrentTimeline()) {
4234         getCurrentTimeline()->controller()->startPreviewRender();
4235     }
4236 }
4237 
4238 void MainWindow::showTimelineToolbarMenu(const QPoint &pos)
4239 {
4240     QMenu menu;
4241     menu.addAction(actionCollection()->action(KStandardAction::name(KStandardAction::ConfigureToolbars)));
4242     QMenu *contextSize = new QMenu(i18n("Icon Size"));
4243     menu.addMenu(contextSize);
4244     auto *sizeGroup = new QActionGroup(contextSize);
4245     int currentSize = m_timelineToolBar->iconSize().width();
4246     QAction *a = new QAction(i18nc("@item:inmenu Icon size", "Default"), contextSize);
4247     a->setData(m_timelineToolBar->iconSizeDefault());
4248     a->setCheckable(true);
4249     if (m_timelineToolBar->iconSizeDefault() == currentSize) {
4250         a->setChecked(true);
4251     }
4252     a->setActionGroup(sizeGroup);
4253     contextSize->addAction(a);
4254     KIconTheme *theme = KIconLoader::global()->theme();
4255     QList<int> avSizes;
4256     if (theme) {
4257         avSizes = theme->querySizes(KIconLoader::Toolbar);
4258     }
4259 
4260     std::sort(avSizes.begin(), avSizes.end());
4261 
4262     if (avSizes.count() < 10) {
4263         // Fixed or threshold type icons
4264         for (int it : avSizes) {
4265             QString text;
4266             if (it < 19) {
4267                 text = i18n("Small (%1x%2)", it, it);
4268             } else if (it < 25) {
4269                 text = i18n("Medium (%1x%2)", it, it);
4270             } else if (it < 35) {
4271                 text = i18n("Large (%1x%2)", it, it);
4272             } else {
4273                 text = i18n("Huge (%1x%2)", it, it);
4274             }
4275 
4276             // save the size in the contextIconSizes map
4277             auto *sizeAction = new QAction(text, contextSize);
4278             sizeAction->setData(it);
4279             sizeAction->setCheckable(true);
4280             sizeAction->setActionGroup(sizeGroup);
4281             if (it == currentSize) {
4282                 sizeAction->setChecked(true);
4283             }
4284             contextSize->addAction(sizeAction);
4285         }
4286     } else {
4287         // Scalable icons.
4288         const int progression[] = {16, 22, 32, 48, 64, 96, 128, 192, 256};
4289 
4290         for (int i : progression) {
4291             for (int it : avSizes) {
4292                 if (it >= i) {
4293                     QString text;
4294                     if (it < 19) {
4295                         text = i18n("Small (%1x%2)", it, it);
4296                     } else if (it < 25) {
4297                         text = i18n("Medium (%1x%2)", it, it);
4298                     } else if (it < 35) {
4299                         text = i18n("Large (%1x%2)", it, it);
4300                     } else {
4301                         text = i18n("Huge (%1x%2)", it, it);
4302                     }
4303 
4304                     // save the size in the contextIconSizes map
4305                     auto *sizeAction = new QAction(text, contextSize);
4306                     sizeAction->setData(it);
4307                     sizeAction->setCheckable(true);
4308                     sizeAction->setActionGroup(sizeGroup);
4309                     if (it == currentSize) {
4310                         sizeAction->setChecked(true);
4311                     }
4312                     contextSize->addAction(sizeAction);
4313                     break;
4314                 }
4315             }
4316         }
4317     }
4318     KEditToolBar::setGlobalDefaultToolBar("timelineToolBar");
4319     connect(contextSize, &QMenu::triggered, this, &MainWindow::setTimelineToolbarIconSize);
4320     menu.exec(m_timelineToolBar->mapToGlobal(pos));
4321     contextSize->deleteLater();
4322 }
4323 
4324 void MainWindow::setTimelineToolbarIconSize(QAction *a)
4325 {
4326     if (!a) {
4327         return;
4328     }
4329     int size = a->data().toInt();
4330     m_timelineToolBar->setIconDimensions(size);
4331     KSharedConfigPtr config = KSharedConfig::openConfig();
4332     KConfigGroup mainConfig(config, QStringLiteral("MainWindow"));
4333     KConfigGroup tbGroup(&mainConfig, QStringLiteral("Toolbar timelineToolBar"));
4334     m_timelineToolBar->saveSettings(tbGroup);
4335 }
4336 
4337 void MainWindow::slotManageCache()
4338 {
4339     QPointer<TemporaryData> d(new TemporaryData(pCore->currentDoc(), false, this));
4340     connect(d, &TemporaryData::disableProxies, this, &MainWindow::slotDisableProxies);
4341     d->exec();
4342 }
4343 
4344 void MainWindow::slotUpdateCompositing(bool checked)
4345 {
4346     getCurrentTimeline()->controller()->switchCompositing(checked);
4347     pCore->currentDoc()->setModified();
4348 }
4349 
4350 void MainWindow::slotUpdateCompositeAction(bool enable)
4351 {
4352     m_compositeAction->setChecked(enable);
4353 }
4354 
4355 void MainWindow::showMenuBar(bool show)
4356 {
4357     if (!show && toolBar()->isHidden()) {
4358         KMessageBox::information(this, i18n("This will hide the menu bar completely. You can show it again by typing Ctrl+M."), i18n("Hide menu bar"),
4359                                  QStringLiteral("show-menubar-warning"));
4360     }
4361     menuBar()->setVisible(show);
4362 }
4363 
4364 void MainWindow::forceIconSet(bool force)
4365 {
4366     KdenliveSettings::setForce_breeze(force);
4367     if (force) {
4368         // Check current color theme
4369         QColor background = qApp->palette().window().color();
4370         bool useDarkIcons = background.value() < 100;
4371         KdenliveSettings::setUse_dark_breeze(useDarkIcons);
4372     }
4373     if (KMessageBox::warningContinueCancel(this, i18n("Kdenlive needs to be restarted to apply the icon theme change. Restart now?")) ==
4374         KMessageBox::Continue) {
4375         slotRestart();
4376     }
4377 }
4378 
4379 TimelineWidget *MainWindow::getCurrentTimeline() const
4380 {
4381     return m_timelineTabs->getCurrentTimeline();
4382 }
4383 
4384 TimelineWidget *MainWindow::getTimeline(const QUuid uuid) const
4385 {
4386     return m_timelineTabs->getTimeline(uuid);
4387 }
4388 
4389 bool MainWindow::hasTimeline() const
4390 {
4391     return m_timelineTabs != nullptr;
4392 }
4393 
4394 void MainWindow::closeTimelineTab(const QUuid uuid)
4395 {
4396     m_timelineTabs->closeTimelineTab(uuid);
4397 }
4398 
4399 const QStringList MainWindow::openedSequences() const
4400 {
4401     if (m_timelineTabs) {
4402         return m_timelineTabs->openedSequences();
4403     }
4404     return QStringList();
4405 }
4406 
4407 void MainWindow::resetTimelineTracks()
4408 {
4409     TimelineWidget *current = getCurrentTimeline();
4410     if (current) {
4411         current->controller()->resetTrackHeight();
4412     }
4413 }
4414 
4415 void MainWindow::slotRemapItemTime()
4416 {
4417     TimelineWidget *current = getCurrentTimeline();
4418     if (current) {
4419         current->controller()->remapItemTime(-1);
4420     }
4421 }
4422 
4423 void MainWindow::slotEditItemSpeed()
4424 {
4425     TimelineWidget *current = getCurrentTimeline();
4426     if (current) {
4427         current->controller()->changeItemSpeed(-1, -1);
4428     }
4429 }
4430 
4431 void MainWindow::slotSwitchTimelineZone(bool active)
4432 {
4433     pCore->currentDoc()->setDocumentProperty(QStringLiteral("enableTimelineZone"), active ? QStringLiteral("1") : QStringLiteral("0"));
4434     Q_EMIT getCurrentTimeline()->controller()->useRulerChanged();
4435     QSignalBlocker blocker(m_useTimelineZone);
4436     m_useTimelineZone->setActive(active);
4437 }
4438 
4439 void MainWindow::slotGrabItem()
4440 {
4441     getCurrentTimeline()->controller()->grabCurrent();
4442 }
4443 
4444 void MainWindow::slotCollapse()
4445 {
4446     if ((QApplication::focusWidget() != nullptr) && (QApplication::focusWidget()->parentWidget() != nullptr) &&
4447         QApplication::focusWidget()->parentWidget() == pCore->bin()) {
4448         // Bin expand/collapse?
4449 
4450     } else {
4451         QWidget *widget = QApplication::focusWidget();
4452         while ((widget != nullptr) && widget != this) {
4453             if (widget == m_effectStackDock) {
4454                 m_assetPanel->collapseCurrentEffect();
4455                 return;
4456             }
4457             widget = widget->parentWidget();
4458         }
4459 
4460         // Collapse / expand track
4461         getCurrentTimeline()->controller()->collapseActiveTrack();
4462     }
4463 }
4464 
4465 void MainWindow::slotExpandClip()
4466 {
4467     getCurrentTimeline()->controller()->expandActiveClip();
4468 }
4469 
4470 bool MainWindow::timelineVisible() const
4471 {
4472     return !centralWidget()->isHidden();
4473 }
4474 
4475 void MainWindow::slotActivateAudioTrackSequence()
4476 {
4477     auto *action = qobject_cast<QAction *>(sender());
4478     const QList<int> trackIds = getCurrentTimeline()->model()->getTracksIds(true);
4479     int trackPos = qBound(0, action->data().toInt(), trackIds.count() - 1);
4480     int tid = trackIds.at(trackPos);
4481     getCurrentTimeline()->controller()->setActiveTrack(tid);
4482 }
4483 
4484 void MainWindow::slotActivateVideoTrackSequence()
4485 {
4486     auto *action = qobject_cast<QAction *>(sender());
4487     const QList<int> trackIds = getCurrentTimeline()->model()->getTracksIds(false);
4488     int trackPos = qBound(0, action->data().toInt(), trackIds.count() - 1);
4489     int tid = trackIds.at(trackIds.count() - 1 - trackPos);
4490     getCurrentTimeline()->controller()->setActiveTrack(tid);
4491     if (m_activeTool == ToolType::MulticamTool) {
4492         pCore->monitorManager()->slotPerformMultiTrackMode();
4493     }
4494 }
4495 
4496 void MainWindow::slotActivateTarget()
4497 {
4498     auto *action = qobject_cast<QAction *>(sender());
4499     if (action) {
4500         int ix = action->data().toInt();
4501         getCurrentTimeline()->controller()->assignCurrentTarget(ix);
4502     }
4503 }
4504 
4505 void MainWindow::resetSubtitles(const QUuid &uuid)
4506 {
4507     // Hide subtitle track
4508     m_buttonSubtitleEditTool->setChecked(false);
4509     KdenliveSettings::setShowSubtitles(false);
4510     pCore->subtitleWidget()->setModel(nullptr);
4511     if (pCore->currentDoc()) {
4512         std::shared_ptr<TimelineItemModel> timeline = pCore->currentDoc()->getTimeline(uuid);
4513         if (timeline && timeline->hasSubtitleModel()) {
4514             auto subModel = timeline->getSubtitleModel();
4515             QMap<std::pair<int, QString>, QString> currentSubs = subModel->getSubtitlesList();
4516             QMapIterator<std::pair<int, QString>, QString> i(currentSubs);
4517             while (i.hasNext()) {
4518                 i.next();
4519                 const QString workPath = pCore->currentDoc()->subTitlePath(uuid, i.key().first, false);
4520                 QFile workFile(workPath);
4521                 if (workFile.exists()) {
4522                     workFile.remove();
4523                 }
4524             }
4525         }
4526     }
4527 }
4528 
4529 void MainWindow::slotShowSubtitles(bool show)
4530 {
4531     const QUuid uuid = getCurrentTimeline()->model()->uuid();
4532     KdenliveSettings::setShowSubtitles(show);
4533     if (getCurrentTimeline()->model()->hasSubtitleModel()) {
4534         getCurrentTimeline()->connectSubtitleModel(false);
4535     } else {
4536         QMap<QString, QString> props = QMap<QString, QString>();
4537         slotEditSubtitle(props);
4538     }
4539     pCore->currentDoc()->setSequenceProperty(uuid, QStringLiteral("hidesubtitle"), show ? 0 : 1);
4540 }
4541 
4542 void MainWindow::slotInitSubtitle(const QMap<QString, QString> &subProperties, const QUuid &uuid)
4543 {
4544     std::shared_ptr<TimelineItemModel> timeline = pCore->currentDoc()->getTimeline(uuid);
4545     Q_ASSERT(!timeline->hasSubtitleModel());
4546     std::shared_ptr<SubtitleModel> subtitleModel = timeline->createSubtitleModel();
4547     // Starting a new subtitle for this project
4548     pCore->subtitleWidget()->setModel(subtitleModel);
4549     subtitleModel->loadProperties(subProperties);
4550     if (uuid == pCore->currentTimelineId() && pCore->currentDoc()->getSequenceProperty(uuid, QStringLiteral("hidesubtitle")).toInt() == 0) {
4551         KdenliveSettings::setShowSubtitles(true);
4552         m_buttonSubtitleEditTool->setChecked(true);
4553         getCurrentTimeline()->connectSubtitleModel(true);
4554     }
4555 }
4556 
4557 void MainWindow::slotEditSubtitle(const QMap<QString, QString> &subProperties)
4558 {
4559     bool hasSubtitleModel = getCurrentTimeline()->hasSubtitles();
4560     if (!hasSubtitleModel) {
4561         std::shared_ptr<SubtitleModel> subtitleModel = getCurrentTimeline()->model()->createSubtitleModel();
4562         // Starting a new subtitle for this project
4563         pCore->subtitleWidget()->setModel(subtitleModel);
4564         m_buttonSubtitleEditTool->setChecked(true);
4565         KdenliveSettings::setShowSubtitles(true);
4566         if (!subProperties.isEmpty()) {
4567             subtitleModel->loadProperties(subProperties);
4568             // Load the disabled / locked state of the subtitle
4569             Q_EMIT getCurrentTimeline()->controller()->subtitlesLockedChanged();
4570             Q_EMIT getCurrentTimeline()->controller()->subtitlesDisabledChanged();
4571         }
4572         getCurrentTimeline()->connectSubtitleModel(true);
4573     } else {
4574         KdenliveSettings::setShowSubtitles(m_buttonSubtitleEditTool->isChecked());
4575         getCurrentTimeline()->connectSubtitleModel(false);
4576     }
4577 }
4578 
4579 void MainWindow::slotAddSubtitle(const QString &text)
4580 {
4581     showSubtitleTrack();
4582     getCurrentTimeline()->model()->getSubtitleModel()->addSubtitle(-1, text);
4583 }
4584 
4585 void MainWindow::slotDisableSubtitle()
4586 {
4587     getCurrentTimeline()->controller()->switchSubtitleDisable();
4588 }
4589 
4590 void MainWindow::slotLockSubtitle()
4591 {
4592     getCurrentTimeline()->controller()->switchSubtitleLock();
4593 }
4594 
4595 void MainWindow::showSubtitleTrack()
4596 {
4597     if (!getCurrentTimeline()->hasSubtitles() || !m_buttonSubtitleEditTool->isChecked()) {
4598         m_buttonSubtitleEditTool->setChecked(true);
4599         slotEditSubtitle();
4600     }
4601 }
4602 
4603 void MainWindow::slotImportSubtitle()
4604 {
4605     showSubtitleTrack();
4606     getCurrentTimeline()->controller()->importSubtitle();
4607 }
4608 
4609 void MainWindow::slotManageSubtitle()
4610 {
4611     showSubtitleTrack();
4612     getCurrentTimeline()->controller()->subtitlesMenuActivated(-1);
4613 }
4614 
4615 void MainWindow::slotExportSubtitle()
4616 {
4617     if (!getCurrentTimeline()->hasSubtitles()) {
4618         pCore->displayMessage(i18n("No subtitles in current project"), ErrorMessage);
4619         return;
4620     }
4621     getCurrentTimeline()->controller()->exportSubtitle();
4622 }
4623 
4624 void MainWindow::slotSpeechRecognition()
4625 {
4626     if (!getCurrentTimeline()->hasSubtitles()) {
4627         slotEditSubtitle();
4628     }
4629     getCurrentTimeline()->controller()->subtitleSpeechRecognition();
4630 }
4631 
4632 void MainWindow::slotCopyDebugInfo()
4633 {
4634     QString debuginfo = QStringLiteral("Kdenlive: %1\n").arg(KAboutData::applicationData().version());
4635     QString packageType = pCore->packageType();
4636     debuginfo.append(QStringLiteral("Package Type: %1\n").arg(packageType.isEmpty() ? QStringLiteral("Unknown/Default") : packageType));
4637     debuginfo.append(QStringLiteral("MLT: %1\n").arg(mlt_version_get_string()));
4638     debuginfo.append(QStringLiteral("Qt: %1 (built against %2 %3)\n").arg(QString::fromLocal8Bit(qVersion()), QT_VERSION_STR, QSysInfo::buildAbi()));
4639     debuginfo.append(QStringLiteral("Frameworks: %2\n").arg(KCoreAddons::versionString()));
4640     debuginfo.append(QStringLiteral("System: %1\n").arg(QSysInfo::prettyProductName()));
4641     debuginfo.append(QStringLiteral("Kernel: %1 %2\n").arg(QSysInfo::kernelType(), QSysInfo::kernelVersion()));
4642     debuginfo.append(QStringLiteral("CPU: %1\n").arg(QSysInfo::currentCpuArchitecture()));
4643     debuginfo.append(QStringLiteral("Windowing System: %1\n").arg(QGuiApplication::platformName()));
4644     if (m_clipMonitor) {
4645         debuginfo.append(QStringLiteral("GPU: %1\n").arg(m_clipMonitor->getGPUInfo().join(QLatin1Char('/'))));
4646     }
4647     debuginfo.append(QStringLiteral("Movit (GPU): %1\n").arg(KdenliveSettings::gpu_accel() ? QStringLiteral("enabled") : QStringLiteral("disabled")));
4648     debuginfo.append(QStringLiteral("Track Compositing: %1\n").arg(TransitionsRepository::get()->getCompositingTransition()));
4649     QClipboard *clipboard = QApplication::clipboard();
4650     clipboard->setText(debuginfo);
4651 }
4652 
4653 bool MainWindow::eventFilter(QObject *object, QEvent *event)
4654 {
4655     switch (event->type()) {
4656     case QEvent::ShortcutOverride:
4657         if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape) {
4658             if (pCore->isMediaMonitoring()) {
4659                 slotShowTrackRec(false);
4660                 return true;
4661             }
4662             if (pCore->isMediaCapturing()) {
4663                 pCore->switchCapture();
4664                 return true;
4665             }
4666             if (m_activeTool != ToolType::SelectTool && m_commandStack->activeStack()->canUndo()) {
4667                 m_buttonSelectTool->trigger();
4668                 return true;
4669             } else {
4670                 if (m_commandStack->activeStack()->canUndo()) {
4671                     // Don't call selection clear if a drag operation is in progress
4672                     getCurrentTimeline()->model()->requestClearSelection();
4673                 }
4674                 return true;
4675             }
4676         }
4677         break;
4678     default:
4679         break;
4680     }
4681     return QObject::eventFilter(object, event);
4682 }
4683 
4684 void MainWindow::slotRemoveBinDock(const QString &name)
4685 {
4686     QWidget *toDelete = nullptr;
4687     int ix = 0;
4688     for (auto &b : m_binWidgets) {
4689         if (b->parentWidget()->objectName() == name) {
4690             toDelete = b->parentWidget();
4691             m_binWidgets.takeAt(ix);
4692             break;
4693         }
4694         ix++;
4695     }
4696     if (toDelete) {
4697         toDelete->deleteLater();
4698     }
4699     updateDockMenu();
4700     loadDockActions();
4701 }
4702 
4703 void MainWindow::addBin(Bin *bin, const QString &binName)
4704 {
4705     connect(bin, &Bin::findInTimeline, this, &MainWindow::slotClipInTimeline, Qt::DirectConnection);
4706     connect(bin, &Bin::setupTargets, this, [&](bool hasVideo, QMap<int, QString> audioStreams) {
4707         if (getCurrentTimeline() && getCurrentTimeline()->controller()) {
4708             getCurrentTimeline()->controller()->setTargetTracks(hasVideo, audioStreams);
4709         }
4710     });
4711     if (!m_binWidgets.isEmpty()) {
4712         // This is a secondary bin widget
4713         int ix = binCount() + 1;
4714         QDockWidget *binDock = addDock(binName.isEmpty() ? i18n("Project Bin %1", ix) : binName, QString("project_bin_%1").arg(ix), bin);
4715         bin->setupGeneratorMenu();
4716         connect(bin, &Bin::requestShowEffectStack, m_assetPanel, &AssetPanel::showEffectStack);
4717         connect(bin, &Bin::requestShowClipProperties, getBin(), &Bin::showClipProperties);
4718         connect(bin, &Bin::requestBinClose, this, [this, binDock]() { Q_EMIT removeBinDock(binDock->objectName()); });
4719         tabifyDockWidget(m_projectBinDock, binDock);
4720         // Disable title bar since it is tabbed
4721         binDock->setTitleBarWidget(new QWidget);
4722         // Update dock list
4723         updateDockMenu();
4724         loadDockActions();
4725         binDock->show();
4726         binDock->raise();
4727     }
4728     m_binWidgets << bin;
4729 }
4730 
4731 void MainWindow::tabifyBins()
4732 {
4733     QList<QDockWidget *> docks = findChildren<QDockWidget *>();
4734     for (auto dock : qAsConst(docks)) {
4735         if (dock->objectName().startsWith(QLatin1String("project_bin_"))) {
4736             tabifyDockWidget(m_projectBinDock, dock);
4737         }
4738     }
4739 }
4740 
4741 Bin *MainWindow::getBin()
4742 {
4743     if (m_binWidgets.isEmpty()) {
4744         return nullptr;
4745     }
4746     return m_binWidgets.first();
4747 }
4748 
4749 Bin *MainWindow::activeBin()
4750 {
4751     QWidget *wid = QApplication::focusWidget();
4752     if (wid) {
4753         for (auto &bin : m_binWidgets) {
4754             if (bin == wid || bin->isAncestorOf(wid)) {
4755                 return bin;
4756             }
4757         }
4758     }
4759     return m_binWidgets.first();
4760 }
4761 
4762 int MainWindow::binCount() const
4763 {
4764     if (m_binWidgets.isEmpty()) {
4765         return 0;
4766     }
4767     return m_binWidgets.count();
4768 }
4769 
4770 void MainWindow::processRestoreState(const QByteArray &state)
4771 {
4772     // On Wayland, restoreState crashes when quickly hiding/showing/hiding a monitor in restoreState, so hide before restoring
4773     m_projectMonitorDock->close();
4774     m_clipMonitorDock->close();
4775     restoreState(state);
4776 }
4777 
4778 void MainWindow::checkMaxCacheSize()
4779 {
4780     if (KdenliveSettings::lastCacheCheck().daysTo(QDateTime::currentDateTime()) < 14) {
4781         return;
4782     }
4783     if (KdenliveSettings::checkForUpdate()) {
4784         // Check if the Kdenlive version is very old
4785         const QStringList kdenliveVersion = KAboutData::applicationData().version().split(QLatin1Char('.'));
4786         if (kdenliveVersion.size() > 2) {
4787             bool ok;
4788             int kdenliveYear = kdenliveVersion.at(0).toInt(&ok);
4789             if (ok) {
4790                 int kdenliveMonth = kdenliveVersion.at(1).toInt(&ok);
4791                 if (ok) {
4792                     if (kdenliveYear < 100) {
4793                         kdenliveYear += 2000;
4794                     }
4795                     QDate releaseDate = QDate(kdenliveYear, kdenliveMonth, 1);
4796                     if (releaseDate.isValid()) {
4797                         int days = releaseDate.daysTo(QDate::currentDate());
4798                         if (days > 180) {
4799                             // Propose update
4800                             QAction *updateAction = new QAction(i18n("Go to download page"), this);
4801                             connect(updateAction, &QAction::triggered, this,
4802                                     []() { QDesktopServices::openUrl(QUrl(QStringLiteral("https://kdenlive.org/download"))); });
4803                             QAction *abortAction = new QAction(i18n("Never check again"), this);
4804                             connect(abortAction, &QAction::triggered, this, []() { KdenliveSettings::setCheckForUpdate(false); });
4805                             if (days > 360) {
4806                                 pCore->displayBinMessage(i18n("Your Kdenlive version is older than 1 year, we strongly encourage you to upgrade"),
4807                                                          KMessageWidget::Warning, {updateAction, abortAction}, true, BinMessage::BinCategory::UpdateMessage);
4808                             } else {
4809                                 pCore->displayBinMessage(i18n("Your Kdenlive version is older than 6 months, we encourage you to upgrade"),
4810                                                          KMessageWidget::Information, {updateAction, abortAction}, true,
4811                                                          BinMessage::BinCategory::UpdateMessage);
4812                             }
4813                         }
4814                     }
4815                 }
4816             }
4817         }
4818     }
4819 
4820     KdenliveSettings::setLastCacheCheck(QDateTime::currentDateTime());
4821     // Check cached data size
4822     if (KdenliveSettings::maxcachesize() <= 0) {
4823         return;
4824     }
4825     bool ok;
4826     KIO::filesize_t total = 0;
4827     QDir cacheDir = pCore->currentDoc()->getCacheDir(SystemCacheRoot, &ok);
4828     if (!ok) {
4829         return;
4830     }
4831     QDir backupFolder(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/.backup"));
4832     QList<QDir> toAdd;
4833     QList<QDir> toRemove;
4834     if (cacheDir.exists()) {
4835         toAdd << cacheDir;
4836     }
4837     if (backupFolder.exists()) {
4838         toAdd << cacheDir;
4839     }
4840     if (cacheDir.cd(QStringLiteral("knewstuff"))) {
4841         toRemove << cacheDir;
4842         cacheDir.cdUp();
4843     }
4844     if (cacheDir.cd(QStringLiteral("attica"))) {
4845         toRemove << cacheDir;
4846         cacheDir.cdUp();
4847     }
4848     if (cacheDir.cd(QStringLiteral("proxy"))) {
4849         toRemove << cacheDir;
4850         cacheDir.cdUp();
4851     }
4852     pCore->displayMessage(i18n("Checking cached data size"), InformationMessage);
4853     while (!toAdd.isEmpty()) {
4854         QDir dir = toAdd.takeFirst();
4855         KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(dir.absolutePath()));
4856         job->exec();
4857         total += job->totalSize();
4858     }
4859     while (!toRemove.isEmpty()) {
4860         QDir dir = toRemove.takeFirst();
4861         KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(dir.absolutePath()));
4862         job->exec();
4863         total -= job->totalSize();
4864     }
4865     if (total > KIO::filesize_t(1048576) * KdenliveSettings::maxcachesize()) {
4866         slotManageCache();
4867     }
4868 }
4869 
4870 void MainWindow::manageClipJobs(AbstractTask::JOBTYPE type, QWidget *parentWidget)
4871 {
4872     QScopedPointer<ClipJobManager> dialog(new ClipJobManager(type, parentWidget ? parentWidget : this));
4873     dialog->exec();
4874     // Rebuild list of clip jobs
4875     buildDynamicActions();
4876     loadClipActions();
4877 }
4878 
4879 TimelineWidget *MainWindow::openTimeline(const QUuid &uuid, const QString &tabName, std::shared_ptr<TimelineItemModel> timelineModel)
4880 {
4881     // Create a new timeline tab
4882     KdenliveDoc *project = pCore->currentDoc();
4883     TimelineWidget *timeline = m_timelineTabs->addTimeline(uuid, tabName, timelineModel, pCore->monitorManager()->projectMonitor()->getControllerProxy());
4884     slotSetZoom(project->zoom(uuid).x(), false);
4885     getCurrentTimeline()->controller()->setZone(project->zone(uuid), false);
4886     getCurrentTimeline()->controller()->setScrollPos(project->getSequenceProperty(uuid, QStringLiteral("scrollPos")).toInt());
4887     m_projectMonitor->slotLoadClipZone(project->zone(uuid));
4888     return timeline;
4889 }
4890 
4891 bool MainWindow::raiseTimeline(const QUuid &uuid)
4892 {
4893     return m_timelineTabs->raiseTimeline(uuid);
4894 }
4895 
4896 void MainWindow::connectTimeline()
4897 {
4898     qDebug() << "::::::::::: connecting timeline: " << getCurrentTimeline()->getUuid() << ", DUR: " << getCurrentTimeline()->controller()->duration();
4899     if (!getCurrentTimeline()->model()) {
4900         qDebug() << "::::::::::: TIMELINE HAS NO MODEL";
4901     } else {
4902         getCurrentTimeline()->model()->rebuildMixer();
4903     }
4904     // We just switched timeline, ensure a monitor effects view does not remain from previous
4905     m_projectMonitor->resetScene();
4906     const QUuid uuid = getCurrentTimeline()->getUuid();
4907     pCore->projectManager()->setActiveTimeline(uuid);
4908     connect(m_projectMonitor, &Monitor::multitrackView, getCurrentTimeline()->controller(), &TimelineController::slotMultitrackView, Qt::UniqueConnection);
4909     connect(m_projectMonitor, &Monitor::activateTrack, getCurrentTimeline()->controller(), &TimelineController::activateTrackAndSelect, Qt::UniqueConnection);
4910     connect(getCurrentTimeline()->controller(), &TimelineController::timelineClipSelected, this, [&](bool selected) {
4911         m_loopClip->setEnabled(selected);
4912         Q_EMIT pCore->library()->enableAddSelection(selected);
4913     });
4914     connect(pCore->library(), &LibraryWidget::saveTimelineSelection, getCurrentTimeline()->controller(), &TimelineController::saveTimelineSelection,
4915             Qt::UniqueConnection);
4916     getCurrentTimeline()->controller()->clipActions = kdenliveCategoryMap.value(QStringLiteral("timelineselection"))->actions();
4917     connect(getCurrentTimeline()->controller(), &TimelineController::durationChanged, pCore->projectManager(), &ProjectManager::adjustProjectDuration);
4918     connect(pCore->bin(), &Bin::processDragEnd, getCurrentTimeline(), &TimelineWidget::endDrag);
4919     pCore->monitorManager()->activateMonitor(Kdenlive::ProjectMonitor);
4920 
4921     KdenliveDoc *project = pCore->currentDoc();
4922     QSignalBlocker blocker(m_zoomSlider);
4923     m_zoomSlider->setValue(project->zoom(uuid).x());
4924     int position = project->getSequenceProperty(uuid, QStringLiteral("position"), QString::number(0)).toInt();
4925     pCore->monitorManager()->projectMonitor()->adjustRulerSize(getCurrentTimeline()->model()->duration() - 1, project->getFilteredGuideModel(uuid));
4926     pCore->monitorManager()->projectMonitor()->loadZone(getCurrentTimeline()->controller()->zoneIn(), getCurrentTimeline()->controller()->zoneOut());
4927     pCore->monitorManager()->projectMonitor()->setProducer(getCurrentTimeline()->model()->producer(), position);
4928     connect(project, &KdenliveDoc::docModified, this, &MainWindow::slotUpdateDocumentState);
4929     slotUpdateDocumentState(project->isModified());
4930 
4931     // Timeline preview
4932     QAction *previewRender = actionCollection()->action(QStringLiteral("prerender_timeline_zone"));
4933     if (previewRender) {
4934         previewRender->setEnabled(true);
4935     }
4936     QAction *disablePreview = actionCollection()->action(QStringLiteral("disable_preview"));
4937     if (getCurrentTimeline()->model()->hasTimelinePreview()) {
4938         disablePreview->setEnabled(true);
4939     } else {
4940         disablePreview->setEnabled(false);
4941     }
4942     disablePreview->blockSignals(true);
4943     disablePreview->setChecked(false);
4944     disablePreview->blockSignals(false);
4945 
4946     // Ensure the active timeline has an opaque black background for compositing
4947     getCurrentTimeline()->model()->makeTransparentBg(false);
4948 
4949     // Audio record actions
4950     connect(pCore.get(), &Core::finalizeRecording, getCurrentTimeline()->controller(), &TimelineController::finishRecording);
4951     connect(pCore.get(), &Core::recordAudio, getCurrentTimeline()->controller(), &TimelineController::switchRecording);
4952 
4953     // switch to active subtitle model
4954     pCore->subtitleWidget()->setModel(getCurrentTimeline()->model()->getSubtitleModel());
4955     bool hasSubtitleModel = getCurrentTimeline()->hasSubtitles();
4956     Q_EMIT getCurrentTimeline()->controller()->subtitlesLockedChanged();
4957     Q_EMIT getCurrentTimeline()->controller()->subtitlesDisabledChanged();
4958     bool showSubs = project->getSequenceProperty(uuid, QStringLiteral("hidesubtitle")).toInt() == 0;
4959     KdenliveSettings::setShowSubtitles(showSubs && hasSubtitleModel);
4960     getCurrentTimeline()->connectSubtitleModel(hasSubtitleModel);
4961     m_buttonSubtitleEditTool->setChecked(showSubs && hasSubtitleModel);
4962     if (hasSubtitleModel) {
4963         // Restore style
4964         getCurrentTimeline()->model()->getSubtitleModel()->loadProperties({});
4965         slotShowSubtitles(showSubs);
4966     }
4967     // Display timeline guides in the guides list
4968     pCore->guidesList()->setModel(project->getGuideModel(uuid), project->getFilteredGuideModel(uuid));
4969 
4970     if (m_renderWidget) {
4971         slotCheckRenderStatus();
4972         m_renderWidget->setGuides(project->getGuideModel(uuid));
4973         m_renderWidget->updateDocumentPath();
4974         m_renderWidget->showRenderDuration();
4975     }
4976 }
4977 
4978 void MainWindow::disconnectTimeline(TimelineWidget *timeline)
4979 {
4980     // Save current tab timeline position
4981     if (pCore->currentDoc()) {
4982         // pCore->currentDoc()->position = pCore->getTimelinePosition();
4983         //  disconnect(pCore->currentDoc(), &KdenliveDoc::docModified, this, &MainWindow::slotUpdateDocumentState);
4984         // qDebug()<<"=== SETTING POSITION  FOR DOC: "<<pCore->currentDoc()->position<<" / "<<pCore->currentDoc()->uuid;
4985     }
4986     // Ensure the active timeline has an transparent black background for embedded compositing
4987     timeline->model()->makeTransparentBg(true);
4988     disconnect(timeline->controller(), &TimelineController::durationChanged, pCore->projectManager(), &ProjectManager::adjustProjectDuration);
4989     disconnect(m_projectMonitor, &Monitor::multitrackView, timeline->controller(), &TimelineController::slotMultitrackView);
4990     disconnect(m_projectMonitor, &Monitor::activateTrack, timeline->controller(), &TimelineController::activateTrackAndSelect);
4991     disconnect(pCore->library(), &LibraryWidget::saveTimelineSelection, timeline->controller(), &TimelineController::saveTimelineSelection);
4992     timeline->controller()->clipActions = QList<QAction *>();
4993     disconnect(pCore->bin(), &Bin::processDragEnd, timeline, &TimelineWidget::endDrag);
4994     pCore->monitorManager()->projectMonitor()->setProducer(nullptr, -2);
4995     // Audio record actions
4996     disconnect(pCore.get(), &Core::finalizeRecording, timeline->controller(), &TimelineController::finishRecording);
4997     disconnect(pCore.get(), &Core::recordAudio, timeline->controller(), &TimelineController::switchRecording);
4998 }
4999 
5000 void MainWindow::appHelpActivated()
5001 {
5002     // Don't use default help, show our website
5003     // QDesktopServices::openUrl(QUrl(QStringLiteral("help:kdenlive")));
5004     if (pCore->packageType() == QStringLiteral("appimage")) {
5005         qDebug() << "::::: LAUNCHING APPIMAGE BROWSER.........";
5006         QProcess process;
5007         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
5008         qDebug() << "::: GOT ENV: " << env.value("LD_LIBRARY_PATH") << ", PATH: " << env.value("PATH") << "\n\nXDG:\n" << env.value("XDG_DATA_DIRS");
5009         QStringList libPath = env.value(QStringLiteral("LD_LIBRARY_PATH")).split(QLatin1Char(':'), Qt::SkipEmptyParts);
5010         QStringList updatedLDPath;
5011         for (auto &s : libPath) {
5012             if (!s.startsWith(QStringLiteral("/tmp/.mount_"))) {
5013                 updatedLDPath << s;
5014             }
5015         }
5016         if (updatedLDPath.isEmpty()) {
5017             env.remove(QStringLiteral("LD_LIBRARY_PATH"));
5018         } else {
5019             env.insert(QStringLiteral("LD_LIBRARY_PATH"), updatedLDPath.join(QLatin1Char(':')));
5020         }
5021         // Path
5022         libPath = env.value(QStringLiteral("PATH")).split(QLatin1Char(':'), Qt::SkipEmptyParts);
5023         updatedLDPath.clear();
5024         for (auto &s : libPath) {
5025             if (!s.startsWith(QStringLiteral("/tmp/.mount_"))) {
5026                 updatedLDPath << s;
5027             }
5028         }
5029         if (updatedLDPath.isEmpty()) {
5030             env.remove(QStringLiteral("PATH"));
5031         } else {
5032             env.insert(QStringLiteral("PATH"), updatedLDPath.join(QLatin1Char(':')));
5033         }
5034         // XDG
5035         libPath = env.value(QStringLiteral("XDG_DATA_DIRS")).split(QLatin1Char(':'), Qt::SkipEmptyParts);
5036         updatedLDPath.clear();
5037         for (auto &s : libPath) {
5038             if (!s.startsWith(QStringLiteral("/tmp/.mount_"))) {
5039                 updatedLDPath << s;
5040             }
5041         }
5042         if (updatedLDPath.isEmpty()) {
5043             env.remove(QStringLiteral("XDG_DATA_DIRS"));
5044         } else {
5045             env.insert(QStringLiteral("XDG_DATA_DIRS"), updatedLDPath.join(QLatin1Char(':')));
5046         }
5047         env.remove(QStringLiteral("QT_QPA_PLATFORM"));
5048         process.setProcessEnvironment(env);
5049         QString openPath = QStandardPaths::findExecutable(QStringLiteral("xdg-open"));
5050         qDebug() << "------------\nFOUND OPEN PATH: " << openPath;
5051         process.setProgram(openPath.isEmpty() ? QStringLiteral("xdg-open") : openPath);
5052         process.setArguments({QStringLiteral("https://docs.kdenlive.org")});
5053         process.startDetached();
5054     } else {
5055         QDesktopServices::openUrl(QUrl(QStringLiteral("https://docs.kdenlive.org")));
5056     }
5057 }
5058 
5059 void MainWindow::slotCreateSequenceFromSelection()
5060 {
5061     pCore->projectManager()->slotCreateSequenceFromSelection();
5062 }
5063 
5064 #ifdef DEBUG_MAINW
5065 #undef DEBUG_MAINW
5066 #endif