File indexing completed on 2024-12-08 04:27:17
0001 /* 0002 SPDX-FileCopyrightText: 2014 Till Theato <root@ttill.de> 0003 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 */ 0005 0006 #include "core.h" 0007 #include "audiomixer/mixermanager.hpp" 0008 #include "bin/bin.h" 0009 #include "bin/mediabrowser.h" 0010 #include "bin/projectitemmodel.h" 0011 #include "capture/mediacapture.h" 0012 #include "dialogs/proxytest.h" 0013 #include "dialogs/subtitleedit.h" 0014 #include "dialogs/textbasededit.h" 0015 #include "dialogs/timeremap.h" 0016 #include "doc/docundostack.hpp" 0017 #include "doc/kdenlivedoc.h" 0018 #include "kdenlive_debug.h" 0019 #include "kdenlivesettings.h" 0020 #include "library/librarywidget.h" 0021 #include "mainwindow.h" 0022 #include "mltconnection.h" 0023 #include "mltcontroller/clipcontroller.h" 0024 #include "monitor/monitormanager.h" 0025 #include "profiles/profilemodel.hpp" 0026 #include "profiles/profilerepository.hpp" 0027 #include "project/dialogs/guideslist.h" 0028 #include "project/projectmanager.h" 0029 #include "timeline2/model/timelineitemmodel.hpp" 0030 #include "timeline2/view/timelinecontroller.h" 0031 #include "timeline2/view/timelinewidget.h" 0032 #include <mlt++/MltRepository.h> 0033 0034 #include "utils/KMessageBox_KdenliveCompat.h" 0035 #include <KMessageBox> 0036 #include <QCoreApplication> 0037 #include <QDir> 0038 #include <QInputDialog> 0039 #include <QQuickStyle> 0040 #include <locale> 0041 #ifdef Q_OS_MAC 0042 #include <xlocale.h> 0043 #endif 0044 0045 std::unique_ptr<Core> Core::m_self; 0046 Core::Core(const QString &packageType) 0047 : audioThumbCache(QStringLiteral("audioCache"), 2000000) 0048 , taskManager(this) 0049 , m_packageType(packageType) 0050 , m_capture(new MediaCapture(this)) 0051 { 0052 } 0053 0054 void Core::prepareShutdown() 0055 { 0056 m_guiConstructed = false; 0057 // m_mainWindow->getCurrentTimeline()->controller()->prepareClose(); 0058 projectItemModel()->blockSignals(true); 0059 QThreadPool::globalInstance()->clear(); 0060 } 0061 0062 void Core::finishShutdown() 0063 { 0064 if (m_monitorManager) { 0065 delete m_monitorManager; 0066 } 0067 if (m_projectManager) { 0068 delete m_projectManager; 0069 } 0070 ClipController::mediaUnavailable.reset(); 0071 } 0072 0073 Core::~Core() {} 0074 0075 bool Core::build(const QString &packageType, bool testMode) 0076 { 0077 if (m_self) { 0078 return true; 0079 } 0080 m_self.reset(new Core(packageType)); 0081 m_self->initLocale(); 0082 0083 qRegisterMetaType<audioShortVector>("audioShortVector"); 0084 qRegisterMetaType<QVector<double>>("QVector<double>"); 0085 qRegisterMetaType<QList<QAction *>>("QList<QAction*>"); 0086 qRegisterMetaType<MessageType>("MessageType"); 0087 qRegisterMetaType<stringMap>("stringMap"); 0088 qRegisterMetaType<audioByteArray>("audioByteArray"); 0089 qRegisterMetaType<QList<ItemInfo>>("QList<ItemInfo>"); 0090 qRegisterMetaType<std::shared_ptr<Mlt::Producer>>("std::shared_ptr<Mlt::Producer>"); 0091 qRegisterMetaType<QVector<int>>(); 0092 qRegisterMetaType<QDomElement>("QDomElement"); 0093 qRegisterMetaType<requestClipInfo>("requestClipInfo"); 0094 qRegisterMetaType<QVector<QPair<QString, QVariant>>>("paramVector"); 0095 qRegisterMetaType<ProfileParam *>("ProfileParam*"); 0096 0097 if (!testMode) { 0098 // Check if we had a crash 0099 QFile lockFile(QDir::temp().absoluteFilePath(QStringLiteral("kdenlivelock"))); 0100 if (lockFile.exists()) { 0101 // a previous instance crashed, propose some actions 0102 if (KdenliveSettings::gpu_accel()) { 0103 // Propose to disable movit 0104 if (KMessageBox::questionTwoActions(QApplication::activeWindow(), 0105 i18n("Kdenlive crashed on last startup.\nDo you want to disable experimental GPU processing (Movit) ?"), {}, 0106 KGuiItem(i18n("Disable GPU processing")), KStandardGuiItem::cont()) == KMessageBox::PrimaryAction) { 0107 KdenliveSettings::setGpu_accel(false); 0108 } 0109 } else { 0110 // propose to delete config files 0111 if (KMessageBox::questionTwoActions(QApplication::activeWindow(), 0112 i18n("Kdenlive crashed on last startup.\nDo you want to reset the configuration files ?"), {}, 0113 KStandardGuiItem::reset(), KStandardGuiItem::cont()) == KMessageBox::PrimaryAction) { 0114 // Release startup crash lock file 0115 QFile lockFile(QDir::temp().absoluteFilePath(QStringLiteral("kdenlivelock"))); 0116 lockFile.remove(); 0117 return false; 0118 } 0119 } 0120 } else { 0121 // Create lock file 0122 lockFile.open(QFile::WriteOnly); 0123 lockFile.write(QByteArray()); 0124 lockFile.close(); 0125 } 0126 } 0127 0128 m_self->m_projectItemModel = ProjectItemModel::construct(); 0129 m_self->m_projectManager = new ProjectManager(m_self.get()); 0130 return true; 0131 } 0132 0133 void Core::initHeadless(const QUrl &url) 0134 { 0135 MltConnection::construct(QString()); 0136 m_projectManager = new ProjectManager(this); 0137 QMetaObject::invokeMethod(pCore->projectManager(), "slotLoadHeadless", Qt::QueuedConnection, Q_ARG(QUrl, url)); 0138 } 0139 0140 void Core::initGUI(bool inSandbox, const QString &MltPath, const QUrl &Url, const QString &clipsToLoad) 0141 { 0142 m_mainWindow = new MainWindow(); 0143 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0144 0145 QStringList styles = QQuickStyle::availableStyles(); 0146 if (styles.contains(QLatin1String("org.kde.desktop"))) { 0147 QQuickStyle::setStyle("org.kde.desktop"); 0148 } else if (styles.contains(QLatin1String("Fusion"))) { 0149 QQuickStyle::setStyle("Fusion"); 0150 } 0151 // ELSE Qt6 see: https://doc.qt.io/qt-6/qtquickcontrols-changes-qt6.html#custom-styles-are-now-proper-qml-modules 0152 #endif 0153 0154 connect(this, &Core::showConfigDialog, m_mainWindow, &MainWindow::slotShowPreferencePage); 0155 0156 Bin *bin = new Bin(m_projectItemModel, m_mainWindow); 0157 m_mainWindow->addBin(bin); 0158 0159 connect(bin, &Bin::requestShowClipProperties, bin, &Bin::showClipProperties); 0160 connect(m_projectItemModel.get(), &ProjectItemModel::refreshPanel, m_mainWindow->activeBin(), &Bin::refreshPanel); 0161 connect(m_projectItemModel.get(), &ProjectItemModel::refreshClip, m_mainWindow->activeBin(), &Bin::refreshClip); 0162 connect(m_projectItemModel.get(), &ProjectItemModel::itemDropped, m_mainWindow->activeBin(), &Bin::slotItemDropped, Qt::QueuedConnection); 0163 connect(m_projectItemModel.get(), &ProjectItemModel::urlsDropped, m_mainWindow->activeBin(), &Bin::slotUrlsDropped, Qt::QueuedConnection); 0164 0165 connect(m_projectItemModel.get(), &ProjectItemModel::effectDropped, m_mainWindow->activeBin(), &Bin::slotEffectDropped); 0166 connect(m_projectItemModel.get(), &ProjectItemModel::addTag, m_mainWindow->activeBin(), &Bin::slotTagDropped); 0167 connect(m_projectItemModel.get(), &QAbstractItemModel::dataChanged, m_mainWindow->activeBin(), &Bin::slotItemEdited); 0168 0169 m_monitorManager = new MonitorManager(this); 0170 if (!Url.isEmpty()) { 0171 Q_EMIT loadingMessageNewStage(i18n("Loading project…")); 0172 } 0173 projectManager()->init(Url, clipsToLoad); 0174 0175 // The MLT Factory will be initiated there, all MLT classes will be usable only after this 0176 if (inSandbox) { 0177 // In a sandbox environment we need to search some paths recursively 0178 QString appPath = qApp->applicationDirPath(); 0179 KdenliveSettings::setFfmpegpath(QDir::cleanPath(appPath + QStringLiteral("/ffmpeg"))); 0180 KdenliveSettings::setFfplaypath(QDir::cleanPath(appPath + QStringLiteral("/ffplay"))); 0181 KdenliveSettings::setFfprobepath(QDir::cleanPath(appPath + QStringLiteral("/ffprobe"))); 0182 KdenliveSettings::setMeltpath(QDir::cleanPath(appPath + QStringLiteral("/melt"))); 0183 m_mainWindow->init(QDir::cleanPath(appPath + QStringLiteral("/../share/mlt/profiles"))); 0184 } else { 0185 // Open connection with Mlt 0186 m_mainWindow->init(MltPath); 0187 } 0188 m_projectItemModel->buildPlaylist(QUuid()); 0189 // load the profiles from disk 0190 ProfileRepository::get()->refresh(); 0191 // load default profile 0192 m_profile = KdenliveSettings::default_profile(); 0193 // load default profile and ask user to select one if not found. 0194 if (m_profile.isEmpty()) { 0195 m_profile = ProjectManager::getDefaultProjectFormat(); 0196 KdenliveSettings::setDefault_profile(m_profile); 0197 } 0198 setCurrentProfile(m_profile); 0199 profileChanged(); 0200 resetThumbProfile(); 0201 0202 if (!ProfileRepository::get()->profileExists(m_profile)) { 0203 KMessageBox::error(m_mainWindow, i18n("The default profile of Kdenlive is not set or invalid, press OK to set it to a correct value.")); 0204 0205 // TODO this simple widget should be improved and probably use profileWidget 0206 // we get the list of profiles 0207 QVector<QPair<QString, QString>> all_profiles = ProfileRepository::get()->getAllProfiles(); 0208 QStringList all_descriptions; 0209 for (const auto &profile : qAsConst(all_profiles)) { 0210 all_descriptions << profile.first; 0211 } 0212 0213 // ask the user 0214 bool ok; 0215 QString item = QInputDialog::getItem(m_mainWindow, i18nc("@title:window", "Select Default Profile"), i18n("Profile:"), all_descriptions, 0, false, &ok); 0216 if (ok) { 0217 ok = false; 0218 for (const auto &profile : qAsConst(all_profiles)) { 0219 if (profile.first == item) { 0220 m_profile = profile.second; 0221 ok = true; 0222 } 0223 } 0224 } 0225 if (!ok) { 0226 KMessageBox::error( 0227 m_mainWindow, 0228 i18n("The given profile is invalid. We default to the profile \"dv_pal\", but you can change this from Kdenlive's settings panel")); 0229 m_profile = QStringLiteral("dv_pal"); 0230 } 0231 KdenliveSettings::setDefault_profile(m_profile); 0232 profileChanged(); 0233 } 0234 // Init producer shown for unavailable media 0235 // TODO make it a more proper image, it currently causes a crash on exit 0236 ClipController::mediaUnavailable = std::make_shared<Mlt::Producer>(ProfileRepository::get()->getProfile(m_self->m_profile)->profile(), "color:blue"); 0237 ClipController::mediaUnavailable->set("length", 99999999); 0238 0239 if (qApp->isSessionRestored()) { 0240 // NOTE: we are restoring only one window, because Kdenlive only uses one MainWindow 0241 m_mainWindow->restore(1, false); 0242 } 0243 m_guiConstructed = true; 0244 QMetaObject::invokeMethod(pCore->projectManager(), "slotLoadOnOpen", Qt::QueuedConnection); 0245 m_mainWindow->show(); 0246 bin->slotUpdatePalette(); 0247 Q_EMIT m_mainWindow->GUISetupDone(); 0248 } 0249 0250 void Core::buildDocks() 0251 { 0252 // Mixer 0253 m_mixerWidget = new MixerManager(m_mainWindow); 0254 connect(m_capture.get(), &MediaCapture::recordStateChanged, m_mixerWidget, &MixerManager::recordStateChanged); 0255 connect(m_mixerWidget, &MixerManager::updateRecVolume, m_capture.get(), &MediaCapture::setAudioVolume); 0256 connect(m_monitorManager, &MonitorManager::cleanMixer, m_mixerWidget, &MixerManager::clearMixers); 0257 m_mixerWidget->checkAudioLevelVersion(); 0258 0259 // Media Browser 0260 m_mediaBrowser = new MediaBrowser(m_mainWindow); 0261 0262 // Library 0263 m_library = new LibraryWidget(m_projectManager, m_mainWindow); 0264 connect(m_library, SIGNAL(addProjectClips(QList<QUrl>)), m_mainWindow->getBin(), SLOT(droppedUrls(QList<QUrl>))); 0265 connect(this, &Core::updateLibraryPath, m_library, &LibraryWidget::slotUpdateLibraryPath); 0266 m_library->setupActions(); 0267 0268 // Subtitles 0269 m_subtitleWidget = new SubtitleEdit(m_mainWindow); 0270 connect(m_subtitleWidget, &SubtitleEdit::addSubtitle, m_mainWindow, &MainWindow::slotAddSubtitle); 0271 connect(m_subtitleWidget, &SubtitleEdit::cutSubtitle, this, [this](int id, int cursorPos) { 0272 if (m_guiConstructed && m_mainWindow->getCurrentTimeline()->controller()) { 0273 if (cursorPos <= 0) { 0274 m_mainWindow->getCurrentTimeline()->controller()->requestClipCut(id, -1); 0275 } else { 0276 m_mainWindow->getCurrentTimeline()->model()->getSubtitleModel()->doCutSubtitle(id, cursorPos); 0277 } 0278 } 0279 }); 0280 0281 // Text edit speech 0282 m_textEditWidget = new TextBasedEdit(m_mainWindow); 0283 0284 // Time remap 0285 m_timeRemapWidget = new TimeRemap(m_mainWindow); 0286 0287 // Guides 0288 m_guidesList = new GuidesList(m_mainWindow); 0289 } 0290 0291 void Core::buildLumaThumbs(const QStringList &values) 0292 { 0293 for (auto &entry : values) { 0294 if (MainWindow::m_lumacache.contains(entry)) { 0295 continue; 0296 } 0297 QImage pix(entry); 0298 if (!pix.isNull()) { 0299 MainWindow::m_lumacache.insert(entry, pix.scaled(50, 30, Qt::KeepAspectRatio, Qt::SmoothTransformation)); 0300 } 0301 } 0302 } 0303 0304 QString Core::openExternalApp(QString appPath, QStringList args) 0305 { 0306 QProcess process; 0307 if (QFileInfo(appPath).isRelative()) { 0308 QString updatedPath = QStandardPaths::findExecutable(appPath); 0309 if (updatedPath.isEmpty()) { 0310 return i18n("Cannot open file %1", appPath); 0311 } 0312 appPath = updatedPath; 0313 } 0314 #if defined(Q_OS_MACOS) 0315 args.prepend(QStringLiteral("--args")); 0316 args.prepend(appPath); 0317 args.prepend(QStringLiteral("-a")); 0318 process.setProgram("open"); 0319 #else 0320 process.setProgram(appPath); 0321 #endif 0322 process.setArguments(args); 0323 if (pCore->packageType() == QStringLiteral("appimage")) { 0324 // Strip appimage custom LD_LIBRARY_PATH... 0325 QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 0326 qDebug() << "::: GOT ENV: " << env.value("LD_LIBRARY_PATH") << ", PATH: " << env.value("PATH"); 0327 QStringList libPath = env.value(QStringLiteral("LD_LIBRARY_PATH")).split(QLatin1Char(':'), Qt::SkipEmptyParts); 0328 QStringList updatedLDPath; 0329 for (auto &s : libPath) { 0330 if (!s.startsWith(QStringLiteral("/tmp/.mount_"))) { 0331 updatedLDPath << s; 0332 } 0333 } 0334 if (updatedLDPath.isEmpty()) { 0335 env.remove(QStringLiteral("LD_LIBRARY_PATH")); 0336 } else { 0337 env.insert(QStringLiteral("LD_LIBRARY_PATH"), updatedLDPath.join(QLatin1Char(':'))); 0338 } 0339 libPath = env.value(QStringLiteral("PATH")).split(QLatin1Char(':'), Qt::SkipEmptyParts); 0340 updatedLDPath.clear(); 0341 for (auto &s : libPath) { 0342 if (!s.startsWith(QStringLiteral("/tmp/.mount_"))) { 0343 updatedLDPath << s; 0344 } 0345 } 0346 if (updatedLDPath.isEmpty()) { 0347 env.remove(QStringLiteral("PATH")); 0348 } else { 0349 env.insert(QStringLiteral("PATH"), updatedLDPath.join(QLatin1Char(':'))); 0350 } 0351 process.setProcessEnvironment(env); 0352 } 0353 qDebug() << "Starting external app" << appPath << "with arguments" << args; 0354 if (!process.startDetached()) { 0355 return process.errorString(); 0356 } 0357 return QString(); 0358 } 0359 0360 const QString Core::nameForLumaFile(const QString &filename) 0361 { 0362 static QMap<QString, QString> names; 0363 names.insert("square2-bars.pgm", i18nc("Luma transition name", "Square 2 Bars")); 0364 names.insert("checkerboard_small.pgm", i18nc("Luma transition name", "Checkerboard Small")); 0365 names.insert("horizontal_blinds.pgm", i18nc("Luma transition name", "Horizontal Blinds")); 0366 names.insert("radial.pgm", i18nc("Luma transition name", "Radial")); 0367 names.insert("linear_x.pgm", i18nc("Luma transition name", "Linear X")); 0368 names.insert("bi-linear_x.pgm", i18nc("Luma transition name", "Bi-Linear X")); 0369 names.insert("linear_y.pgm", i18nc("Luma transition name", "Linear Y")); 0370 names.insert("bi-linear_y.pgm", i18nc("Luma transition name", "Bi-Linear Y")); 0371 names.insert("square.pgm", i18nc("Luma transition name", "Square")); 0372 names.insert("square2.pgm", i18nc("Luma transition name", "Square 2")); 0373 names.insert("cloud.pgm", i18nc("Luma transition name", "Cloud")); 0374 names.insert("symmetric_clock.pgm", i18nc("Luma transition name", "Symmetric Clock")); 0375 names.insert("radial-bars.pgm", i18nc("Luma transition name", "Radial Bars")); 0376 names.insert("spiral.pgm", i18nc("Luma transition name", "Spiral")); 0377 names.insert("spiral2.pgm", i18nc("Luma transition name", "Spiral 2")); 0378 names.insert("curtain.pgm", i18nc("Luma transition name", "Curtain")); 0379 names.insert("burst.pgm", i18nc("Luma transition name", "Burst")); 0380 names.insert("clock.pgm", i18nc("Luma transition name", "Clock")); 0381 0382 names.insert("luma01.pgm", i18nc("Luma transition name", "Bar Horizontal")); 0383 names.insert("luma02.pgm", i18nc("Luma transition name", "Bar Vertical")); 0384 names.insert("luma03.pgm", i18nc("Luma transition name", "Barn Door Horizontal")); 0385 names.insert("luma04.pgm", i18nc("Luma transition name", "Barn Door Vertical")); 0386 names.insert("luma05.pgm", i18nc("Luma transition name", "Barn Door Diagonal SW-NE")); 0387 names.insert("luma06.pgm", i18nc("Luma transition name", "Barn Door Diagonal NW-SE")); 0388 names.insert("luma07.pgm", i18nc("Luma transition name", "Diagonal Top Left")); 0389 names.insert("luma08.pgm", i18nc("Luma transition name", "Diagonal Top Right")); 0390 names.insert("luma09.pgm", i18nc("Luma transition name", "Matrix Waterfall Horizontal")); 0391 names.insert("luma10.pgm", i18nc("Luma transition name", "Matrix Waterfall Vertical")); 0392 names.insert("luma11.pgm", i18nc("Luma transition name", "Matrix Snake Horizontal")); 0393 names.insert("luma12.pgm", i18nc("Luma transition name", "Matrix Snake Parallel Horizontal")); 0394 names.insert("luma13.pgm", i18nc("Luma transition name", "Matrix Snake Vertical")); 0395 names.insert("luma14.pgm", i18nc("Luma transition name", "Matrix Snake Parallel Vertical")); 0396 names.insert("luma15.pgm", i18nc("Luma transition name", "Barn V Up")); 0397 names.insert("luma16.pgm", i18nc("Luma transition name", "Iris Circle")); 0398 names.insert("luma17.pgm", i18nc("Luma transition name", "Double Iris")); 0399 names.insert("luma18.pgm", i18nc("Luma transition name", "Iris Box")); 0400 names.insert("luma19.pgm", i18nc("Luma transition name", "Box Bottom Right")); 0401 names.insert("luma20.pgm", i18nc("Luma transition name", "Box Bottom Left")); 0402 names.insert("luma21.pgm", i18nc("Luma transition name", "Box Right Center")); 0403 names.insert("luma22.pgm", i18nc("Luma transition name", "Clock Top")); 0404 0405 return names.contains(filename) ? names.constFind(filename).value() : filename; 0406 } 0407 0408 std::unique_ptr<Core> &Core::self() 0409 { 0410 if (!m_self) { 0411 qWarning() << "Core has not been created"; 0412 } 0413 return m_self; 0414 } 0415 0416 MainWindow *Core::window() 0417 { 0418 return m_mainWindow; 0419 } 0420 0421 ProjectManager *Core::projectManager() 0422 { 0423 return m_projectManager; 0424 } 0425 0426 MonitorManager *Core::monitorManager() 0427 { 0428 return m_monitorManager; 0429 } 0430 0431 Monitor *Core::getMonitor(int id) 0432 { 0433 if (id == Kdenlive::ClipMonitor) { 0434 return m_monitorManager->clipMonitor(); 0435 } 0436 return m_monitorManager->projectMonitor(); 0437 } 0438 0439 void Core::seekMonitor(int id, int position) 0440 { 0441 if (!m_guiConstructed) { 0442 return; 0443 } 0444 if (id == Kdenlive::ProjectMonitor) { 0445 m_monitorManager->projectMonitor()->requestSeek(position); 0446 } else { 0447 m_monitorManager->clipMonitor()->requestSeek(position); 0448 } 0449 } 0450 0451 MediaBrowser *Core::mediaBrowser() 0452 { 0453 if (!m_mainWindow) { 0454 return nullptr; 0455 } 0456 return m_mediaBrowser; 0457 } 0458 0459 Bin *Core::bin() 0460 { 0461 if (!m_mainWindow) { 0462 return nullptr; 0463 } 0464 return m_mainWindow->getBin(); 0465 } 0466 0467 Bin *Core::activeBin() 0468 { 0469 return m_mainWindow->activeBin(); 0470 } 0471 0472 void Core::selectBinClip(const QString &clipId, bool activateMonitor, int frame, const QPoint &zone) 0473 { 0474 m_mainWindow->activeBin()->selectClipById(clipId, frame, zone, activateMonitor); 0475 } 0476 0477 void Core::selectTimelineItem(int id) 0478 { 0479 if (m_guiConstructed && m_mainWindow->getCurrentTimeline() && m_mainWindow->getCurrentTimeline()->model()) { 0480 m_mainWindow->getCurrentTimeline()->model()->requestAddToSelection(id, true); 0481 } 0482 } 0483 0484 LibraryWidget *Core::library() 0485 { 0486 return m_library; 0487 } 0488 0489 GuidesList *Core::guidesList() 0490 { 0491 return m_guidesList; 0492 } 0493 0494 TextBasedEdit *Core::textEditWidget() 0495 { 0496 return m_textEditWidget; 0497 } 0498 0499 TimeRemap *Core::timeRemapWidget() 0500 { 0501 return m_timeRemapWidget; 0502 } 0503 0504 bool Core::currentRemap(const QString &clipId) 0505 { 0506 return m_timeRemapWidget == nullptr ? false : m_timeRemapWidget->currentClip() == clipId; 0507 } 0508 0509 SubtitleEdit *Core::subtitleWidget() 0510 { 0511 return m_subtitleWidget; 0512 } 0513 0514 MixerManager *Core::mixer() 0515 { 0516 return m_mixerWidget; 0517 } 0518 0519 void Core::initLocale() 0520 { 0521 QLocale systemLocale = QLocale(); // For disabling group separator by default 0522 systemLocale.setNumberOptions(QLocale::OmitGroupSeparator); 0523 QLocale::setDefault(systemLocale); 0524 } 0525 0526 ToolType::ProjectTool Core::activeTool() 0527 { 0528 return m_mainWindow->getCurrentTimeline()->activeTool(); 0529 } 0530 0531 const QUuid Core::currentTimelineId() const 0532 { 0533 if (m_projectManager->getTimeline()) { 0534 return m_projectManager->getTimeline()->uuid(); 0535 } 0536 return QUuid(); 0537 } 0538 0539 std::unique_ptr<Mlt::Repository> &Core::getMltRepository() 0540 { 0541 return MltConnection::self()->getMltRepository(); 0542 } 0543 0544 std::unique_ptr<ProfileModel> &Core::getCurrentProfile() const 0545 { 0546 return ProfileRepository::get()->getProfile(m_currentProfile); 0547 } 0548 0549 Mlt::Profile &Core::getMonitorProfile() 0550 { 0551 return m_monitorProfile; 0552 } 0553 0554 Mlt::Profile &Core::getProjectProfile() 0555 { 0556 return m_projectProfile; 0557 } 0558 0559 void Core::updateMonitorProfile() 0560 { 0561 m_monitorProfile.set_colorspace(m_projectProfile.colorspace()); 0562 m_monitorProfile.set_frame_rate(m_projectProfile.frame_rate_num(), m_projectProfile.frame_rate_den()); 0563 m_monitorProfile.set_width(m_projectProfile.width()); 0564 m_monitorProfile.set_height(m_projectProfile.height()); 0565 m_monitorProfile.set_progressive(m_projectProfile.progressive()); 0566 m_monitorProfile.set_sample_aspect(m_projectProfile.sample_aspect_num(), m_projectProfile.sample_aspect_den()); 0567 m_monitorProfile.set_display_aspect(m_projectProfile.display_aspect_num(), m_projectProfile.display_aspect_den()); 0568 m_monitorProfile.set_explicit(true); 0569 Q_EMIT monitorProfileUpdated(); 0570 } 0571 0572 const QString &Core::getCurrentProfilePath() const 0573 { 0574 return m_currentProfile; 0575 } 0576 0577 bool Core::setCurrentProfile(const QString profilePath) 0578 { 0579 if (m_currentProfile == profilePath) { 0580 // no change required, ensure timecode has correct fps 0581 m_timecode.setFormat(getCurrentProfile()->fps()); 0582 Q_EMIT updateProjectTimecode(); 0583 return true; 0584 } 0585 if (ProfileRepository::get()->profileExists(profilePath)) { 0586 // Ensure all running tasks are stopped before attempting a global profile change 0587 taskManager.slotCancelJobs(); 0588 m_currentProfile = profilePath; 0589 std::unique_ptr<ProfileModel> ¤tProfile = getCurrentProfile(); 0590 m_projectProfile.set_colorspace(currentProfile->colorspace()); 0591 m_projectProfile.set_frame_rate(currentProfile->frame_rate_num(), currentProfile->frame_rate_den()); 0592 m_projectProfile.set_height(currentProfile->height()); 0593 m_projectProfile.set_progressive(currentProfile->progressive()); 0594 m_projectProfile.set_sample_aspect(currentProfile->sample_aspect_num(), currentProfile->sample_aspect_den()); 0595 m_projectProfile.set_display_aspect(currentProfile->display_aspect_num(), currentProfile->display_aspect_den()); 0596 m_projectProfile.set_width(currentProfile->width()); 0597 free(m_projectProfile.get_profile()->description); 0598 m_projectProfile.get_profile()->description = strdup(currentProfile->description().toUtf8().constData()); 0599 m_projectProfile.set_explicit(true); 0600 updateMonitorProfile(); 0601 // Regenerate thumbs profile 0602 resetThumbProfile(); 0603 // inform render widget 0604 m_timecode.setFormat(currentProfile->fps()); 0605 profileChanged(); 0606 if (m_guiConstructed) { 0607 Q_EMIT m_mainWindow->updateRenderWidgetProfile(); 0608 m_monitorManager->resetProfiles(); 0609 Q_EMIT m_monitorManager->updatePreviewScaling(); 0610 if (m_mainWindow->hasTimeline() && m_mainWindow->getCurrentTimeline() && m_mainWindow->getCurrentTimeline()->model()) { 0611 // m_mainWindow->getCurrentTimeline()->model()->updateProfile(getProjectProfile()); 0612 m_mainWindow->getCurrentTimeline()->model()->updateFieldOrderFilter(currentProfile); 0613 checkProfileValidity(); 0614 Q_EMIT m_mainWindow->getCurrentTimeline()->controller()->frameFormatChanged(); 0615 } 0616 Q_EMIT updateProjectTimecode(); 0617 } 0618 return true; 0619 } 0620 return false; 0621 } 0622 0623 void Core::checkProfileValidity() 0624 { 0625 int offset = (getProjectProfile().width() % 2) + (getProjectProfile().height() % 2); 0626 if (offset > 0) { 0627 // Profile is broken, warn user 0628 if (m_mainWindow->getBin()) { 0629 Q_EMIT m_mainWindow->getBin()->displayBinMessage(i18n("Your project profile is invalid, rendering might fail."), KMessageWidget::Warning); 0630 } 0631 } 0632 } 0633 0634 double Core::getCurrentSar() const 0635 { 0636 return getCurrentProfile()->sar(); 0637 } 0638 0639 double Core::getCurrentDar() const 0640 { 0641 return getCurrentProfile()->dar(); 0642 } 0643 0644 double Core::getCurrentFps() const 0645 { 0646 return getCurrentProfile()->fps(); 0647 } 0648 0649 QSize Core::getCurrentFrameDisplaySize() const 0650 { 0651 return {qRound(getCurrentProfile()->height() * getCurrentDar()), getCurrentProfile()->height()}; 0652 } 0653 0654 QSize Core::getCurrentFrameSize() const 0655 { 0656 return {getCurrentProfile()->width(), getCurrentProfile()->height()}; 0657 } 0658 0659 void Core::refreshProjectMonitorOnce() 0660 { 0661 if (!m_guiConstructed || currentDoc()->loading || currentDoc()->closing) return; 0662 m_monitorManager->refreshProjectMonitor(); 0663 } 0664 0665 void Core::refreshProjectRange(QPair<int, int> range) 0666 { 0667 if (!m_guiConstructed || currentDoc()->loading || currentDoc()->closing) return; 0668 m_monitorManager->refreshProjectRange(range, true); 0669 } 0670 0671 const QSize Core::getCompositionSizeOnTrack(const ObjectId &id) 0672 { 0673 return m_mainWindow->getCurrentTimeline()->model()->getCompositionSizeOnTrack(id); 0674 } 0675 0676 QPair<int, QString> Core::currentTrackInfo() const 0677 { 0678 if (m_mainWindow->getCurrentTimeline()->controller()) { 0679 int tid = m_mainWindow->getCurrentTimeline()->controller()->activeTrack(); 0680 if (tid >= 0) { 0681 return {m_mainWindow->getCurrentTimeline()->model()->getTrackMltIndex(tid), m_mainWindow->getCurrentTimeline()->model()->getTrackTagById(tid)}; 0682 } 0683 if (m_mainWindow->getCurrentTimeline()->model()->isSubtitleTrack(tid)) { 0684 return {tid, i18n("Subtitles")}; 0685 } 0686 } 0687 return {-1, QString()}; 0688 } 0689 0690 int Core::getItemPosition(const ObjectId &id) 0691 { 0692 switch (id.type) { 0693 case KdenliveObjectType::TimelineClip: 0694 if (currentDoc()->getTimeline(id.uuid)->isClip(id.itemId)) { 0695 return currentDoc()->getTimeline(id.uuid)->getClipPosition(id.itemId); 0696 } else { 0697 qWarning() << "querying non clip properties"; 0698 } 0699 break; 0700 case KdenliveObjectType::TimelineComposition: 0701 if (currentDoc()->getTimeline(id.uuid)->isComposition(id.itemId)) { 0702 return currentDoc()->getTimeline(id.uuid)->getCompositionPosition(id.itemId); 0703 } 0704 break; 0705 case KdenliveObjectType::TimelineMix: 0706 if (currentDoc()->getTimeline(id.uuid)->isClip(id.itemId)) { 0707 return currentDoc()->getTimeline(id.uuid)->getMixInOut(id.itemId).first; 0708 } else { 0709 qWarning() << "querying non clip properties"; 0710 } 0711 break; 0712 case KdenliveObjectType::BinClip: 0713 case KdenliveObjectType::TimelineTrack: 0714 case KdenliveObjectType::Master: 0715 return 0; 0716 default: 0717 qWarning() << "unhandled object type"; 0718 } 0719 return 0; 0720 } 0721 0722 int Core::getItemIn(const ObjectId &id) 0723 { 0724 switch (id.type) { 0725 case KdenliveObjectType::TimelineClip: 0726 if (currentDoc()->getTimeline(id.uuid)->isClip(id.itemId)) { 0727 return currentDoc()->getTimeline(id.uuid)->getClipIn(id.itemId); 0728 } else { 0729 qWarning() << "querying non clip properties"; 0730 } 0731 break; 0732 case KdenliveObjectType::TimelineMix: 0733 case KdenliveObjectType::TimelineComposition: 0734 case KdenliveObjectType::BinClip: 0735 case KdenliveObjectType::TimelineTrack: 0736 case KdenliveObjectType::Master: 0737 return 0; 0738 default: 0739 qWarning() << "unhandled object type"; 0740 } 0741 return 0; 0742 } 0743 0744 int Core::getItemIn(const QUuid &uuid, const ObjectId &id) 0745 { 0746 if (!m_guiConstructed || !currentDoc()->getTimeline(uuid)) { 0747 qWarning() << "GUI not build"; 0748 return 0; 0749 } 0750 switch (id.type) { 0751 case KdenliveObjectType::TimelineClip: 0752 if (currentDoc()->getTimeline(uuid)->isClip(id.itemId)) { 0753 return currentDoc()->getTimeline(uuid)->getClipIn(id.itemId); 0754 } else { 0755 qWarning() << "querying non clip properties"; 0756 } 0757 break; 0758 case KdenliveObjectType::TimelineMix: 0759 case KdenliveObjectType::TimelineComposition: 0760 case KdenliveObjectType::BinClip: 0761 case KdenliveObjectType::TimelineTrack: 0762 case KdenliveObjectType::Master: 0763 return 0; 0764 default: 0765 qWarning() << "unhandled object type"; 0766 } 0767 return 0; 0768 } 0769 0770 PlaylistState::ClipState Core::getItemState(const ObjectId &id) 0771 { 0772 switch (id.type) { 0773 case KdenliveObjectType::TimelineClip: 0774 if (currentDoc()->getTimeline(id.uuid)->isClip(id.itemId)) { 0775 return currentDoc()->getTimeline(id.uuid)->getClipState(id.itemId); 0776 } else { 0777 qWarning() << "querying non clip properties"; 0778 } 0779 break; 0780 case KdenliveObjectType::TimelineComposition: 0781 return PlaylistState::VideoOnly; 0782 case KdenliveObjectType::BinClip: 0783 if (!m_guiConstructed) return PlaylistState::Disabled; 0784 return m_mainWindow->getBin()->getClipState(id.itemId); 0785 case KdenliveObjectType::TimelineTrack: 0786 return currentDoc()->getTimeline(id.uuid)->isAudioTrack(id.itemId) ? PlaylistState::AudioOnly : PlaylistState::VideoOnly; 0787 case KdenliveObjectType::Master: 0788 return PlaylistState::Disabled; 0789 default: 0790 qWarning() << "unhandled object type"; 0791 break; 0792 } 0793 return PlaylistState::Disabled; 0794 } 0795 0796 int Core::getItemDuration(const ObjectId &id) 0797 { 0798 switch (id.type) { 0799 case KdenliveObjectType::TimelineClip: 0800 if (currentDoc()->getTimeline(id.uuid)->isClip(id.itemId)) { 0801 return currentDoc()->getTimeline(id.uuid)->getClipPlaytime(id.itemId); 0802 } else { 0803 qWarning() << "querying non clip properties"; 0804 } 0805 break; 0806 case KdenliveObjectType::TimelineComposition: 0807 if (currentDoc()->getTimeline(id.uuid)->isComposition(id.itemId)) { 0808 return currentDoc()->getTimeline(id.uuid)->getCompositionPlaytime(id.itemId); 0809 } 0810 break; 0811 case KdenliveObjectType::BinClip: 0812 if (!m_guiConstructed) return 0; 0813 return int(m_mainWindow->getBin()->getClipDuration(id.itemId)); 0814 case KdenliveObjectType::TimelineTrack: 0815 case KdenliveObjectType::Master: 0816 return currentDoc()->getTimeline(id.uuid)->duration(); 0817 case KdenliveObjectType::TimelineMix: 0818 if (currentDoc()->getTimeline(id.uuid)->isClip(id.itemId)) { 0819 return currentDoc()->getTimeline(id.uuid)->getMixDuration(id.itemId); 0820 } else { 0821 qWarning() << "querying non clip properties"; 0822 } 0823 break; 0824 default: 0825 qWarning() << "unhandled object type: " << (int)id.type; 0826 } 0827 return 0; 0828 } 0829 0830 QSize Core::getItemFrameSize(const ObjectId &id) 0831 { 0832 switch (id.type) { 0833 case KdenliveObjectType::TimelineClip: 0834 if (currentDoc()->getTimeline(id.uuid)->isClip(id.itemId)) { 0835 return currentDoc()->getTimeline(id.uuid)->getClipFrameSize(id.itemId); 0836 } else { 0837 qWarning() << "querying non clip properties"; 0838 } 0839 break; 0840 case KdenliveObjectType::BinClip: 0841 if (!m_guiConstructed) return QSize(); 0842 return m_mainWindow->getBin()->getFrameSize(id.itemId); 0843 case KdenliveObjectType::TimelineTrack: 0844 case KdenliveObjectType::Master: 0845 case KdenliveObjectType::TimelineComposition: 0846 case KdenliveObjectType::TimelineMix: 0847 return pCore->getCurrentFrameSize(); 0848 default: 0849 qWarning() << "unhandled object type frame size"; 0850 } 0851 return pCore->getCurrentFrameSize(); 0852 } 0853 0854 int Core::getItemTrack(const ObjectId &id) 0855 { 0856 switch (id.type) { 0857 case KdenliveObjectType::TimelineClip: 0858 case KdenliveObjectType::TimelineComposition: 0859 case KdenliveObjectType::TimelineMix: 0860 return currentDoc()->getTimeline(id.uuid)->getItemTrackId(id.itemId); 0861 default: 0862 qWarning() << "unhandled object type"; 0863 } 0864 return 0; 0865 } 0866 0867 void Core::refreshProjectItem(const ObjectId &id) 0868 { 0869 if (!m_guiConstructed || (!id.uuid.isNull() && !m_mainWindow->getTimeline(id.uuid))) return; 0870 switch (id.type) { 0871 case KdenliveObjectType::TimelineClip: 0872 case KdenliveObjectType::TimelineMix: 0873 if (currentDoc()->getTimeline(id.uuid)->isClip(id.itemId)) { 0874 m_mainWindow->getTimeline(id.uuid)->controller()->refreshItem(id.itemId); 0875 } 0876 break; 0877 case KdenliveObjectType::TimelineComposition: 0878 if (currentDoc()->getTimeline(id.uuid)->isComposition(id.itemId)) { 0879 m_mainWindow->getTimeline(id.uuid)->controller()->refreshItem(id.itemId); 0880 } 0881 break; 0882 case KdenliveObjectType::TimelineTrack: 0883 if (m_mainWindow->getTimeline(id.uuid)->model()->isTrack(id.itemId)) { 0884 refreshProjectMonitorOnce(); 0885 } 0886 break; 0887 case KdenliveObjectType::BinClip: 0888 if (m_monitorManager->clipMonitorVisible()) { 0889 m_monitorManager->activateMonitor(Kdenlive::ClipMonitor); 0890 m_monitorManager->refreshClipMonitor(true); 0891 } 0892 if (m_monitorManager->projectMonitorVisible() && m_mainWindow->getCurrentTimeline()->controller()->refreshIfVisible(id.itemId)) { 0893 m_monitorManager->refreshTimer.start(); 0894 } 0895 break; 0896 case KdenliveObjectType::Master: 0897 refreshProjectMonitorOnce(); 0898 break; 0899 default: 0900 qWarning() << "unhandled object type"; 0901 } 0902 } 0903 0904 bool Core::hasTimelinePreview() const 0905 { 0906 if (!m_guiConstructed) { 0907 return false; 0908 } 0909 return m_mainWindow->getCurrentTimeline()->controller()->renderedChunks().size() > 0; 0910 } 0911 0912 KdenliveDoc *Core::currentDoc() 0913 { 0914 return m_projectManager->current(); 0915 } 0916 0917 Timecode Core::timecode() const 0918 { 0919 return m_timecode; 0920 } 0921 0922 void Core::setDocumentModified() 0923 { 0924 m_projectManager->current()->setModified(); 0925 } 0926 0927 int Core::projectDuration() const 0928 { 0929 std::shared_ptr<TimelineItemModel> activeModel = m_projectManager->getTimeline(); 0930 if (activeModel) { 0931 return activeModel->duration(); 0932 } 0933 return 0; 0934 } 0935 0936 void Core::profileChanged() 0937 { 0938 GenTime::setFps(getCurrentFps()); 0939 } 0940 0941 void Core::pushUndo(const Fun &undo, const Fun &redo, const QString &text) 0942 { 0943 undoStack()->push(new FunctionalUndoCommand(undo, redo, text)); 0944 } 0945 0946 void Core::pushUndo(QUndoCommand *command) 0947 { 0948 undoStack()->push(command); 0949 } 0950 0951 int Core::undoIndex() const 0952 { 0953 return m_projectManager->undoStack()->index(); 0954 } 0955 0956 void Core::displaySelectionMessage(const QString &message) 0957 { 0958 if (m_mainWindow) { 0959 Q_EMIT m_mainWindow->displaySelectionMessage(message); 0960 } 0961 } 0962 0963 void Core::displayMessage(const QString &message, MessageType type, int timeout) 0964 { 0965 if (m_mainWindow) { 0966 if (type == ProcessingJobMessage || type == OperationCompletedMessage) { 0967 Q_EMIT m_mainWindow->displayProgressMessage(message, type, timeout); 0968 } else { 0969 Q_EMIT m_mainWindow->displayMessage(message, type, timeout); 0970 } 0971 } else { 0972 qDebug() << message; 0973 } 0974 } 0975 0976 void Core::loadingClips(int count, bool allowInterrupt) 0977 { 0978 Q_EMIT m_mainWindow->displayProgressMessage(i18n("Loading clips"), MessageType::ProcessingJobMessage, count, allowInterrupt); 0979 } 0980 0981 void Core::displayBinMessage(const QString &text, int type, const QList<QAction *> &actions, bool showClose, BinMessage::BinCategory messageCategory) 0982 { 0983 m_mainWindow->getBin()->doDisplayMessage(text, KMessageWidget::MessageType(type), actions, showClose, messageCategory); 0984 } 0985 0986 void Core::displayBinLogMessage(const QString &text, int type, const QString logInfo) 0987 { 0988 m_mainWindow->getBin()->doDisplayMessage(text, KMessageWidget::MessageType(type), logInfo); 0989 } 0990 0991 void Core::clearAssetPanel(int itemId) 0992 { 0993 if (m_guiConstructed) Q_EMIT m_mainWindow->clearAssetPanel(itemId); 0994 } 0995 0996 std::shared_ptr<EffectStackModel> Core::getItemEffectStack(const QUuid &uuid, int itemType, int itemId) 0997 { 0998 if (!m_guiConstructed) return nullptr; 0999 switch (itemType) { 1000 case int(KdenliveObjectType::TimelineClip): 1001 return currentDoc()->getTimeline(uuid)->getClipEffectStack(itemId); 1002 case int(KdenliveObjectType::TimelineTrack): 1003 return currentDoc()->getTimeline(uuid)->getTrackEffectStackModel(itemId); 1004 case int(KdenliveObjectType::BinClip): 1005 return m_projectItemModel->getClipEffectStack(itemId); 1006 case int(KdenliveObjectType::Master): 1007 return currentDoc()->getTimeline(uuid)->getMasterEffectStackModel(); 1008 default: 1009 return nullptr; 1010 } 1011 } 1012 1013 std::shared_ptr<DocUndoStack> Core::undoStack() 1014 { 1015 return projectManager()->undoStack(); 1016 } 1017 1018 QMap<int, QString> Core::getTrackNames(bool videoOnly) 1019 { 1020 if (!m_guiConstructed) return QMap<int, QString>(); 1021 return m_mainWindow->getCurrentTimeline()->controller()->getTrackNames(videoOnly); 1022 } 1023 1024 QPair<int, int> Core::getCompositionATrack(int cid) const 1025 { 1026 if (!m_guiConstructed) return {}; 1027 return m_mainWindow->getCurrentTimeline()->controller()->getCompositionATrack(cid); 1028 } 1029 1030 bool Core::compositionAutoTrack(int cid) const 1031 { 1032 return m_mainWindow->getCurrentTimeline()->controller()->compositionAutoTrack(cid); 1033 } 1034 1035 void Core::setCompositionATrack(int cid, int aTrack) 1036 { 1037 if (!m_guiConstructed) return; 1038 m_mainWindow->getCurrentTimeline()->controller()->setCompositionATrack(cid, aTrack); 1039 } 1040 1041 std::shared_ptr<ProjectItemModel> Core::projectItemModel() 1042 { 1043 return m_projectItemModel; 1044 } 1045 1046 void Core::invalidateRange(QPair<int, int> range) 1047 { 1048 if (!m_guiConstructed || currentDoc()->loading || !m_mainWindow->getCurrentTimeline() || m_mainWindow->getCurrentTimeline()->loading) return; 1049 m_mainWindow->getCurrentTimeline()->model()->invalidateZone(range.first, range.second); 1050 } 1051 1052 void Core::invalidateItem(ObjectId itemId) 1053 { 1054 if (!m_guiConstructed || !m_mainWindow->getCurrentTimeline() || m_mainWindow->getCurrentTimeline()->loading) return; 1055 switch (itemId.type) { 1056 case KdenliveObjectType::TimelineClip: 1057 case KdenliveObjectType::TimelineComposition: 1058 m_mainWindow->getCurrentTimeline()->controller()->invalidateItem(itemId.itemId); 1059 break; 1060 case KdenliveObjectType::TimelineTrack: 1061 m_mainWindow->getCurrentTimeline()->controller()->invalidateTrack(itemId.itemId); 1062 break; 1063 case KdenliveObjectType::BinClip: 1064 m_mainWindow->getBin()->invalidateClip(QString::number(itemId.itemId)); 1065 break; 1066 case KdenliveObjectType::Master: 1067 m_mainWindow->getCurrentTimeline()->model()->invalidateZone(0, -1); 1068 break; 1069 default: 1070 // compositions should not have effects 1071 break; 1072 } 1073 } 1074 1075 double Core::getClipSpeed(int id) const 1076 { 1077 return m_mainWindow->getCurrentTimeline()->model()->getClipSpeed(id); 1078 } 1079 1080 void Core::updateItemKeyframes(ObjectId id) 1081 { 1082 if (id.type == KdenliveObjectType::TimelineClip && m_guiConstructed) { 1083 m_mainWindow->getCurrentTimeline()->controller()->updateClip(id.itemId, {TimelineModel::KeyframesRole}); 1084 } 1085 } 1086 1087 void Core::updateItemModel(ObjectId id, const QString &service) 1088 { 1089 if (m_guiConstructed && id.type == KdenliveObjectType::TimelineClip && !m_mainWindow->getCurrentTimeline()->loading && 1090 service.startsWith(QLatin1String("fade"))) { 1091 bool startFade = service.startsWith(QLatin1String("fadein")) || service.startsWith(QLatin1String("fade_from_")); 1092 m_mainWindow->getCurrentTimeline()->controller()->updateClip(id.itemId, {startFade ? TimelineModel::FadeInRole : TimelineModel::FadeOutRole}); 1093 } 1094 } 1095 1096 void Core::showClipKeyframes(ObjectId id, bool enable) 1097 { 1098 if (id.type == KdenliveObjectType::TimelineClip) { 1099 m_mainWindow->getCurrentTimeline()->controller()->showClipKeyframes(id.itemId, enable); 1100 } else if (id.type == KdenliveObjectType::TimelineComposition) { 1101 m_mainWindow->getCurrentTimeline()->controller()->showCompositionKeyframes(id.itemId, enable); 1102 } 1103 } 1104 1105 void Core::resetThumbProfile() 1106 { 1107 m_thumbProfile.set_colorspace(m_projectProfile.colorspace()); 1108 m_thumbProfile.set_frame_rate(m_projectProfile.frame_rate_num(), m_projectProfile.frame_rate_den()); 1109 double factor = 144. / m_projectProfile.height(); 1110 m_thumbProfile.set_height(144); 1111 int width = qRound(m_projectProfile.width() * factor); 1112 if (width % 2 > 0) { 1113 width++; 1114 } 1115 m_thumbProfile.set_width(width); 1116 m_thumbProfile.set_progressive(m_projectProfile.progressive()); 1117 m_thumbProfile.set_sample_aspect(m_projectProfile.sample_aspect_num(), m_projectProfile.sample_aspect_den()); 1118 m_thumbProfile.set_display_aspect(m_projectProfile.display_aspect_num(), m_projectProfile.display_aspect_den()); 1119 m_thumbProfile.set_explicit(true); 1120 } 1121 1122 Mlt::Profile &Core::thumbProfile() 1123 { 1124 return m_thumbProfile; 1125 } 1126 1127 int Core::getMonitorPosition(Kdenlive::MonitorId id) const 1128 { 1129 if (m_guiConstructed) { 1130 switch (id) { 1131 case Kdenlive::ClipMonitor: 1132 return m_monitorManager->clipMonitor()->position(); 1133 default: 1134 return m_monitorManager->projectMonitor()->position(); 1135 } 1136 } 1137 return 0; 1138 } 1139 1140 void Core::triggerAction(const QString &name) 1141 { 1142 QAction *action = m_mainWindow->actionCollection()->action(name); 1143 if (action) { 1144 action->trigger(); 1145 } 1146 } 1147 1148 const QString Core::actionText(const QString &name) 1149 { 1150 QAction *action = m_mainWindow->actionCollection()->action(name); 1151 if (action) { 1152 return action->toolTip(); 1153 } 1154 return QString(); 1155 } 1156 1157 void Core::addActionToCollection(const QString &name, QAction *action) 1158 { 1159 m_mainWindow->actionCollection()->addAction(name, action); 1160 } 1161 1162 void Core::clean() 1163 { 1164 m_self.reset(); 1165 } 1166 1167 void Core::startMediaCapture(int tid, bool checkAudio, bool checkVideo) 1168 { 1169 Q_UNUSED(checkVideo) 1170 // TODO: fix video capture 1171 /*if (checkAudio && checkVideo) { 1172 m_capture->recordVideo(tid, true); 1173 } else*/ 1174 if (checkAudio) { 1175 m_capture->recordAudio(tid, true); 1176 } 1177 m_mediaCaptureFile = m_capture->getCaptureOutputLocation(); 1178 } 1179 1180 void Core::stopMediaCapture(int tid, bool checkAudio, bool checkVideo) 1181 { 1182 Q_UNUSED(checkVideo) 1183 // TODO: fix video capture 1184 /*if (checkAudio && checkVideo) { 1185 m_capture->recordVideo(tid, false); 1186 } else*/ 1187 if (checkAudio) { 1188 m_capture->recordAudio(tid, false); 1189 } 1190 } 1191 1192 void Core::monitorAudio(int tid, bool monitor) 1193 { 1194 m_mainWindow->getCurrentTimeline()->controller()->switchTrackRecord(tid, monitor); 1195 if (monitor && pCore->monitorManager()->projectMonitor()->isPlaying()) { 1196 pCore->monitorManager()->projectMonitor()->stop(); 1197 } 1198 } 1199 1200 void Core::startRecording() 1201 { 1202 int trackId = m_capture->startCapture(); 1203 m_mainWindow->getCurrentTimeline()->startAudioRecord(trackId); 1204 pCore->monitorManager()->slotPlay(); 1205 } 1206 1207 QStringList Core::getAudioCaptureDevices() 1208 { 1209 return m_capture->getAudioCaptureDevices(); 1210 } 1211 1212 int Core::getMediaCaptureState() 1213 { 1214 return m_capture->getState(); 1215 } 1216 1217 bool Core::isMediaMonitoring() const 1218 { 1219 return m_capture->isMonitoring(); 1220 } 1221 1222 bool Core::isMediaCapturing() const 1223 { 1224 return m_capture->isRecording(); 1225 } 1226 1227 void Core::switchCapture() 1228 { 1229 Q_EMIT recordAudio(-1, !isMediaCapturing()); 1230 } 1231 1232 MediaCapture *Core::getAudioDevice() 1233 { 1234 return m_capture.get(); 1235 } 1236 1237 void Core::resetAudioMonitoring() 1238 { 1239 if (m_capture && m_capture->isMonitoring()) { 1240 m_capture->switchMonitorState(false); 1241 m_capture->switchMonitorState(true); 1242 } 1243 } 1244 1245 QString Core::getProjectFolderName(bool folderForAudio) 1246 { 1247 if (currentDoc()) { 1248 return currentDoc()->projectDataFolder(QStringLiteral(), folderForAudio) + QDir::separator(); 1249 } 1250 return QString(); 1251 } 1252 1253 QString Core::getTimelineClipBinId(int cid) 1254 { 1255 if (!m_guiConstructed) { 1256 return QString(); 1257 } 1258 if (m_mainWindow->getCurrentTimeline()->model()->isClip(cid)) { 1259 return m_mainWindow->getCurrentTimeline()->model()->getClipBinId(cid); 1260 } 1261 return QString(); 1262 } 1263 std::unordered_set<QString> Core::getAllTimelineTracksId() 1264 { 1265 std::unordered_set<int> timelineClipIds = m_mainWindow->getCurrentTimeline()->model()->getItemsInRange(-1, 0); 1266 std::unordered_set<QString> tClipBinIds; 1267 for (int id : timelineClipIds) { 1268 auto idString = m_mainWindow->getCurrentTimeline()->model()->getClipBinId(id); 1269 tClipBinIds.insert(idString); 1270 } 1271 return tClipBinIds; 1272 } 1273 1274 int Core::getDurationFromString(const QString &time) 1275 { 1276 return m_timecode.getFrameCount(time); 1277 } 1278 1279 void Core::processInvalidFilter(const QString &service, const QString &id, const QString &message) 1280 { 1281 if (m_guiConstructed) Q_EMIT m_mainWindow->assetPanelWarning(service, id, message); 1282 } 1283 1284 void Core::updateProjectTags(int previousCount, const QMap<int, QStringList> &tags) 1285 { 1286 if (previousCount > tags.size()) { 1287 // Clear previous tags 1288 for (int i = 1; i <= previousCount; i++) { 1289 QString current = currentDoc()->getDocumentProperty(QString("tag%1").arg(i)); 1290 if (!current.isEmpty()) { 1291 currentDoc()->setDocumentProperty(QString("tag%1").arg(i), QString()); 1292 } 1293 } 1294 } 1295 QMapIterator<int, QStringList> j(tags); 1296 int i = 1; 1297 while (j.hasNext()) { 1298 j.next(); 1299 currentDoc()->setDocumentProperty(QString("tag%1").arg(i), QString("%1:%2").arg(j.value().at(1), j.value().at(2))); 1300 i++; 1301 } 1302 } 1303 1304 std::unique_ptr<Mlt::Producer> Core::getMasterProducerInstance() 1305 { 1306 if (m_guiConstructed && m_mainWindow->getCurrentTimeline()) { 1307 std::unique_ptr<Mlt::Producer> producer( 1308 m_mainWindow->getCurrentTimeline()->controller()->tractor()->cut(0, m_mainWindow->getCurrentTimeline()->controller()->duration() - 1)); 1309 return producer; 1310 } 1311 return nullptr; 1312 } 1313 1314 std::unique_ptr<Mlt::Producer> Core::getTrackProducerInstance(int tid) 1315 { 1316 if (m_guiConstructed && m_mainWindow->getCurrentTimeline()) { 1317 std::unique_ptr<Mlt::Producer> producer(new Mlt::Producer(m_mainWindow->getCurrentTimeline()->controller()->trackProducer(tid))); 1318 return producer; 1319 } 1320 return nullptr; 1321 } 1322 1323 bool Core::enableMultiTrack(bool enable) 1324 { 1325 if (!m_guiConstructed || !m_mainWindow->getCurrentTimeline()) { 1326 return false; 1327 } 1328 bool isMultiTrack = pCore->monitorManager()->isMultiTrack(); 1329 if (isMultiTrack || enable) { 1330 pCore->window()->getCurrentTimeline()->controller()->slotMultitrackView(enable, true); 1331 return true; 1332 } 1333 return false; 1334 } 1335 1336 int Core::audioChannels() 1337 { 1338 if (m_projectManager && m_projectManager->current()) { 1339 return m_projectManager->current()->audioChannels(); 1340 } 1341 return 2; 1342 } 1343 1344 void Core::addGuides(const QList<int> &guides) 1345 { 1346 QMap<GenTime, QString> markers; 1347 for (int pos : guides) { 1348 GenTime p(pos, pCore->getCurrentFps()); 1349 markers.insert(p, pCore->currentDoc()->timecode().getDisplayTimecode(p, false)); 1350 } 1351 m_mainWindow->getCurrentTimeline()->controller()->getModel()->getGuideModel()->addMarkers(markers); 1352 } 1353 1354 void Core::temporaryUnplug(const QList<int> &clipIds, bool hide) 1355 { 1356 window()->getCurrentTimeline()->controller()->temporaryUnplug(clipIds, hide); 1357 } 1358 1359 void Core::transcodeFile(const QString &url) 1360 { 1361 qDebug() << "=== TRANSCODING: " << url; 1362 window()->slotTranscode({url}); 1363 } 1364 1365 void Core::transcodeFriendlyFile(const QString &binId, bool checkProfile) 1366 { 1367 window()->slotFriendlyTranscode(binId, checkProfile); 1368 } 1369 1370 void Core::setWidgetKeyBinding(const QString &mess) 1371 { 1372 window()->setWidgetKeyBinding(mess); 1373 } 1374 1375 void Core::showEffectZone(ObjectId id, QPair<int, int> inOut, bool checked) 1376 { 1377 if (m_guiConstructed && m_mainWindow->getCurrentTimeline() && m_mainWindow->getCurrentTimeline()->controller() && id.type != KdenliveObjectType::BinClip) { 1378 m_mainWindow->getCurrentTimeline()->controller()->showRulerEffectZone(inOut, checked); 1379 } 1380 } 1381 1382 void Core::updateMasterZones() 1383 { 1384 if (m_guiConstructed && m_mainWindow->getCurrentTimeline() && m_mainWindow->getCurrentTimeline()->controller()) { 1385 m_mainWindow->getCurrentTimeline()->controller()->updateMasterZones(m_mainWindow->getCurrentTimeline()->model()->getMasterEffectZones()); 1386 } 1387 } 1388 1389 void Core::testProxies() 1390 { 1391 QScopedPointer<ProxyTest> dialog(new ProxyTest(QApplication::activeWindow())); 1392 dialog->exec(); 1393 } 1394 1395 void Core::resizeMix(int cid, int duration, MixAlignment align, int leftFrames) 1396 { 1397 m_mainWindow->getCurrentTimeline()->controller()->resizeMix(cid, duration, align, leftFrames); 1398 } 1399 1400 MixAlignment Core::getMixAlign(const ObjectId &itemInfo) const 1401 { 1402 return m_mainWindow->getTimeline(itemInfo.uuid)->controller()->getMixAlign(itemInfo.itemId); 1403 } 1404 1405 int Core::getMixCutPos(const ObjectId &itemInfo) const 1406 { 1407 return m_mainWindow->getTimeline(itemInfo.uuid)->controller()->getMixCutPos(itemInfo.itemId); 1408 } 1409 1410 void Core::clearTimeRemap() 1411 { 1412 if (timeRemapWidget()) { 1413 timeRemapWidget()->selectedClip(-1, QUuid()); 1414 } 1415 } 1416 1417 void Core::cleanup() 1418 { 1419 audioThumbCache.clear(); 1420 taskManager.slotCancelJobs(); 1421 if (timeRemapWidget()) { 1422 timeRemapWidget()->selectedClip(-1, QUuid()); 1423 } 1424 if (m_mainWindow && m_mainWindow->getCurrentTimeline()) { 1425 disconnect(m_mainWindow->getCurrentTimeline()->controller(), &TimelineController::durationChanged, m_projectManager, 1426 &ProjectManager::adjustProjectDuration); 1427 m_mainWindow->getCurrentTimeline()->controller()->clipActions.clear(); 1428 } 1429 } 1430 1431 #if KNEWSTUFF_VERSION < QT_VERSION_CHECK(5, 98, 0) 1432 int Core::getNewStuff(const QString &config) 1433 { 1434 return m_mainWindow->getNewStuff(config); 1435 } 1436 #endif 1437 1438 void Core::addBin(const QString &id) 1439 { 1440 Bin *bin = new Bin(m_projectItemModel, m_mainWindow, false); 1441 bin->setupMenu(); 1442 bin->setMonitor(m_monitorManager->clipMonitor()); 1443 const QString folderName = bin->setDocument(pCore->currentDoc(), id); 1444 m_mainWindow->addBin(bin, folderName); 1445 } 1446 1447 void Core::loadTimelinePreview(const QUuid uuid, const QString &chunks, const QString &dirty, bool enablePreview, Mlt::Playlist &playlist) 1448 { 1449 std::shared_ptr<TimelineItemModel> tl = pCore->currentDoc()->getTimeline(uuid); 1450 if (tl) { 1451 tl->loadPreview(chunks, dirty, enablePreview, playlist); 1452 } 1453 } 1454 1455 void Core::updateSequenceAVType(const QUuid &uuid, int tracksCount) 1456 { 1457 if (m_mainWindow) { 1458 pCore->bin()->updateSequenceAVType(uuid, tracksCount); 1459 } 1460 }