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