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