File indexing completed on 2024-03-24 04:54:09
0001 /* 0002 SPDX-FileCopyrightText: 2021 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "kdenlivesettingsdialog.h" 0008 #include "clipcreationdialog.h" 0009 #include "core.h" 0010 #include "dialogs/profilesdialog.h" 0011 #include "doc/kdenlivedoc.h" 0012 #include "kdenlivesettings.h" 0013 #include "mainwindow.h" 0014 #include "monitor/monitor.h" 0015 #include "monitor/monitorproxy.h" 0016 #include "profiles/profilemodel.hpp" 0017 #include "profiles/profilerepository.hpp" 0018 #include "profilesdialog.h" 0019 #include "project/dialogs/guidecategories.h" 0020 #include "project/dialogs/profilewidget.h" 0021 #include "timeline2/view/timelinecontroller.h" 0022 #include "timeline2/view/timelinewidget.h" 0023 #include "wizard.h" 0024 0025 #ifdef USE_V4L 0026 #include "capture/v4lcapture.h" 0027 #endif 0028 0029 #include "KLocalizedString" 0030 #include "kdenlive_debug.h" 0031 #include <KArchive> 0032 #include <KArchiveDirectory> 0033 #include <KIO/DesktopExecParser> 0034 #include <KIO/FileCopyJob> 0035 #include <kio_version.h> 0036 #if KIO_VERSION >= QT_VERSION_CHECK(5, 98, 0) 0037 #include <KIO/JobUiDelegateFactory> 0038 #else 0039 #include <KIO/JobUiDelegate> 0040 #endif 0041 #include "utils/KMessageBox_KdenliveCompat.h" 0042 #include <KIO/OpenUrlJob> 0043 #include <KLineEdit> 0044 #include <KMessageBox> 0045 #include <KOpenWithDialog> 0046 #include <KService> 0047 #include <KTar> 0048 #include <KUrlRequesterDialog> 0049 #include <KZip> 0050 0051 #include <QAction> 0052 #include <QButtonGroup> 0053 #include <QDir> 0054 #include <QGuiApplication> 0055 #include <QInputDialog> 0056 #include <QRegularExpression> 0057 #include <QScreen> 0058 #include <QSize> 0059 #include <QThread> 0060 #include <QTimer> 0061 #include <QtConcurrent> 0062 0063 #include <cstdio> 0064 #include <cstdlib> 0065 #include <fcntl.h> 0066 #include <unistd.h> 0067 0068 #ifdef USE_JOGSHUTTLE 0069 #include "jogshuttle/jogaction.h" 0070 #include "jogshuttle/jogshuttleconfig.h" 0071 #include <QStandardPaths> 0072 #include <linux/input.h> 0073 #include <memory> 0074 #endif 0075 0076 SpeechList::SpeechList(QWidget *parent) 0077 : QListWidget(parent) 0078 { 0079 setAlternatingRowColors(true); 0080 setAcceptDrops(true); 0081 setDropIndicatorShown(true); 0082 viewport()->setAcceptDrops(true); 0083 } 0084 0085 QStringList SpeechList::mimeTypes() const 0086 { 0087 return QStringList() << QStringLiteral("text/uri-list"); 0088 } 0089 0090 void SpeechList::dropEvent(QDropEvent *event) 0091 { 0092 const QMimeData *qMimeData = event->mimeData(); 0093 if (qMimeData->hasUrls()) { 0094 QList<QUrl> urls = qMimeData->urls(); 0095 if (!urls.isEmpty()) { 0096 Q_EMIT getDictionary(urls.takeFirst()); 0097 } 0098 } 0099 } 0100 0101 KdenliveSettingsDialog::KdenliveSettingsDialog(QMap<QString, QString> mappable_actions, bool gpuAllowed, QWidget *parent) 0102 : KConfigDialog(parent, QStringLiteral("settings"), KdenliveSettings::self()) 0103 , m_modified(false) 0104 , m_shuttleModified(false) 0105 , m_voskUpdated(false) 0106 , m_mappable_actions(std::move(mappable_actions)) 0107 { 0108 KdenliveSettings::setV4l_format(0); 0109 0110 initMiscPage(); 0111 initProjectPage(); 0112 initProxyPage(); 0113 0114 QWidget *p3 = new QWidget; 0115 m_configTimeline.setupUi(p3); 0116 m_pageTimeline = addPage(p3, i18n("Timeline"), QStringLiteral("video-display")); 0117 0118 QWidget *p4 = new QWidget; 0119 m_configTools.setupUi(p4); 0120 m_pageTools = addPage(p4, i18n("Tools"), QStringLiteral("tools")); 0121 0122 initEnviromentPage(); 0123 0124 QWidget *p11 = new QWidget; 0125 m_configColors.setupUi(p11); 0126 m_pageColors = addPage(p11, i18n("Colors and Guides"), QStringLiteral("color-management")); 0127 m_guidesCategories = new GuideCategories(nullptr, this); 0128 QVBoxLayout *guidesLayout = new QVBoxLayout(m_configColors.guides_box); 0129 guidesLayout->addWidget(m_guidesCategories); 0130 0131 QWidget *p12 = new QWidget; 0132 m_configSpeech.setupUi(p12); 0133 m_pageSpeech = addPage(p12, i18n("Speech To Text"), QStringLiteral("text-speak")); 0134 0135 QWidget *p7 = new QWidget; 0136 m_configSdl.setupUi(p7); 0137 m_pagePlay = addPage(p7, i18n("Playback"), QStringLiteral("media-playback-start")); 0138 0139 QWidget *p5 = new QWidget; 0140 m_configCapture.setupUi(p5); 0141 m_decklinkProfiles = new EncodingProfilesChooser(this, EncodingProfilesManager::DecklinkCapture, false, QStringLiteral("decklink_profile")); 0142 m_configCapture.decklink_profile_box->addWidget(m_decklinkProfiles); 0143 m_v4lProfiles = new EncodingProfilesChooser(this, EncodingProfilesManager::V4LCapture, false, QStringLiteral("v4l_profile")); 0144 m_configCapture.v4l_profile_box->addWidget(m_v4lProfiles); 0145 m_grabProfiles = new EncodingProfilesChooser(this, EncodingProfilesManager::ScreenCapture, false, QStringLiteral("grab_profile")); 0146 m_configCapture.screen_grab_profile_box->addWidget(m_grabProfiles); 0147 m_pageCapture = addPage(p5, i18n("Capture"), QStringLiteral("media-record")); 0148 0149 initDevices(); 0150 initCapturePage(); 0151 initJogShuttlePage(); 0152 initTranscodePage(); 0153 0154 initSdlPage(gpuAllowed); 0155 initSpeechPage(); 0156 0157 // Config dialog size 0158 KSharedConfigPtr config = KSharedConfig::openConfig(); 0159 KConfigGroup settingsGroup(config, "settings"); 0160 QSize optimalSize; 0161 0162 if (!settingsGroup.exists() || !settingsGroup.hasKey("dialogSize")) { 0163 const QSize screenSize = (QGuiApplication::primaryScreen()->availableSize() * 0.9); 0164 const QSize targetSize = QSize(1024, 708); 0165 optimalSize = targetSize.boundedTo(screenSize); 0166 } else { 0167 optimalSize = settingsGroup.readEntry("dialogSize", QVariant(size())).toSize(); 0168 } 0169 resize(optimalSize); 0170 // Use timers so that the Settings window opens before starting the scripts 0171 checkSpeechDependencies(); 0172 } 0173 0174 // static 0175 bool KdenliveSettingsDialog::getBlackMagicDeviceList(QComboBox *devicelist, bool force) 0176 { 0177 if (!force && !KdenliveSettings::decklink_device_found()) { 0178 return false; 0179 } 0180 Mlt::Profile profile; 0181 Mlt::Producer bm(profile, "decklink"); 0182 int found_devices = 0; 0183 if (bm.is_valid()) { 0184 bm.set("list_devices", 1); 0185 found_devices = bm.get_int("devices"); 0186 } else { 0187 KdenliveSettings::setDecklink_device_found(false); 0188 } 0189 if (found_devices <= 0) { 0190 devicelist->setEnabled(false); 0191 return false; 0192 } 0193 KdenliveSettings::setDecklink_device_found(true); 0194 for (int i = 0; i < found_devices; ++i) { 0195 char *tmp = qstrdup(QStringLiteral("device.%1").arg(i).toUtf8().constData()); 0196 devicelist->addItem(bm.get(tmp)); 0197 delete[] tmp; 0198 } 0199 return true; 0200 } 0201 0202 // static 0203 bool KdenliveSettingsDialog::initAudioRecDevice() 0204 { 0205 QStringList audioDevices = pCore->getAudioCaptureDevices(); 0206 0207 // show a hint to the user to know what to check for in case the device list are empty (common issue) 0208 m_configCapture.labelNoAudioDevices->setVisible(audioDevices.empty()); 0209 0210 m_configCapture.kcfg_defaultaudiocapture->addItems(audioDevices); 0211 connect(m_configCapture.kcfg_defaultaudiocapture, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [&]() { 0212 QString currentDevice = m_configCapture.kcfg_defaultaudiocapture->currentText(); 0213 KdenliveSettings::setDefaultaudiocapture(currentDevice); 0214 }); 0215 QString selectedDevice = KdenliveSettings::defaultaudiocapture(); 0216 int selectedIndex = m_configCapture.kcfg_defaultaudiocapture->findText(selectedDevice); 0217 if (!selectedDevice.isEmpty() && selectedIndex > -1) { 0218 m_configCapture.kcfg_defaultaudiocapture->setCurrentIndex(selectedIndex); 0219 } 0220 return true; 0221 } 0222 0223 void KdenliveSettingsDialog::initMiscPage() 0224 { 0225 QWidget *p1 = new QWidget; 0226 m_configMisc.setupUi(p1); 0227 m_pageMisc = addPage(p1, i18n("Misc"), QStringLiteral("configure")); 0228 0229 m_configMisc.kcfg_use_exiftool->setEnabled(!QStandardPaths::findExecutable(QStringLiteral("exiftool")).isEmpty()); 0230 0231 static const QRegularExpression reg(R"((\+|-)?\d{2}:\d{2}:\d{2}(:||,)\d{2})"); 0232 QValidator *validator = new QRegularExpressionValidator(reg, this); 0233 m_configMisc.kcfg_color_duration->setInputMask(pCore->timecode().mask()); 0234 m_configMisc.kcfg_color_duration->setValidator(validator); 0235 m_configMisc.kcfg_title_duration->setInputMask(pCore->timecode().mask()); 0236 m_configMisc.kcfg_title_duration->setValidator(validator); 0237 m_configMisc.kcfg_transition_duration->setInputMask(pCore->timecode().mask()); 0238 m_configMisc.kcfg_transition_duration->setValidator(validator); 0239 m_configMisc.kcfg_mix_duration->setInputMask(pCore->timecode().mask()); 0240 m_configMisc.kcfg_mix_duration->setValidator(validator); 0241 m_configMisc.kcfg_image_duration->setInputMask(pCore->timecode().mask()); 0242 m_configMisc.kcfg_image_duration->setValidator(validator); 0243 m_configMisc.kcfg_sequence_duration->setInputMask(pCore->timecode().mask()); 0244 m_configMisc.kcfg_sequence_duration->setValidator(validator); 0245 m_configMisc.kcfg_fade_duration->setInputMask(pCore->timecode().mask()); 0246 m_configMisc.kcfg_fade_duration->setValidator(validator); 0247 m_configMisc.kcfg_subtitle_duration->setInputMask(pCore->timecode().mask()); 0248 m_configMisc.kcfg_subtitle_duration->setValidator(validator); 0249 0250 m_configMisc.preferredcomposite->clear(); 0251 m_configMisc.preferredcomposite->addItem(i18n("auto")); 0252 m_configMisc.preferredcomposite->addItems(KdenliveSettings::compositingList()); 0253 0254 if (!KdenliveSettings::preferredcomposite().isEmpty()) { 0255 int ix = m_configMisc.preferredcomposite->findText(KdenliveSettings::preferredcomposite()); 0256 if (ix > -1) { 0257 m_configMisc.preferredcomposite->setCurrentIndex(ix); 0258 } 0259 } 0260 connect(m_configMisc.preferredcomposite, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [&]() { 0261 if (m_configMisc.preferredcomposite->currentText() != KdenliveSettings::preferredcomposite()) { 0262 KdenliveSettings::setPreferredcomposite(m_configMisc.preferredcomposite->currentText()); 0263 int mode = pCore->currentDoc()->getDocumentProperty(QStringLiteral("compositing")).toInt(); 0264 pCore->window()->getCurrentTimeline()->controller()->switchCompositing(mode); 0265 pCore->currentDoc()->setModified(); 0266 } 0267 }); 0268 } 0269 0270 void KdenliveSettingsDialog::initProjectPage() 0271 { 0272 QWidget *p9 = new QWidget; 0273 m_configProject.setupUi(p9); 0274 // Timeline preview 0275 QString currentPreviewData = 0276 KdenliveSettings::previewparams().isEmpty() ? QString() : QString("%1;%2").arg(KdenliveSettings::previewparams(), KdenliveSettings::previewextension()); 0277 m_tlPreviewProfiles = new EncodingTimelinePreviewProfilesChooser(p9, true, currentPreviewData, false); 0278 m_configProject.preview_profile_box->addWidget(m_tlPreviewProfiles); 0279 auto *vbox = new QVBoxLayout; 0280 m_pw = new ProfileWidget(this); 0281 vbox->addWidget(m_pw); 0282 connect(m_pw, &ProfileWidget::profileChanged, this, [this]() { m_tlPreviewProfiles->filterPreviewProfiles(m_pw->selectedProfile()); }); 0283 m_configProject.profile_box->setLayout(vbox); 0284 m_configProject.profile_box->setTitle(i18n("Select the Default Profile (preset)")); 0285 // Select profile 0286 m_pw->loadProfile(KdenliveSettings::default_profile().isEmpty() ? pCore->getCurrentProfile()->path() : KdenliveSettings::default_profile()); 0287 m_tlPreviewProfiles->filterPreviewProfiles(m_pw->selectedProfile()); 0288 connect(m_tlPreviewProfiles, &EncodingTimelinePreviewProfilesChooser::currentIndexChanged, this, &KdenliveSettingsDialog::slotDialogModified); 0289 connect(m_pw, &ProfileWidget::profileChanged, this, &KdenliveSettingsDialog::slotDialogModified); 0290 m_configProject.projecturl->setMode(KFile::Directory); 0291 m_configProject.projecturl->setUrl(QUrl::fromLocalFile(KdenliveSettings::defaultprojectfolder())); 0292 connect(m_configProject.projecturl, &KUrlRequester::textChanged, this, &KdenliveSettingsDialog::slotDialogModified); 0293 connect(m_configProject.kcfg_customprojectfolder, &QCheckBox::toggled, m_configProject.projecturl, &KUrlRequester::setEnabled); 0294 connect(m_configProject.kcfg_videotracks, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this]() { 0295 if (m_configProject.kcfg_videotracks->value() + m_configProject.kcfg_audiotracks->value() <= 0) { 0296 m_configProject.kcfg_videotracks->setValue(1); 0297 } 0298 }); 0299 connect(m_configProject.kcfg_audiotracks, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this]() { 0300 if (m_configProject.kcfg_videotracks->value() + m_configProject.kcfg_audiotracks->value() <= 0) { 0301 m_configProject.kcfg_audiotracks->setValue(1); 0302 } 0303 }); 0304 0305 m_pageProject = addPage(p9, i18n("Project Defaults"), QStringLiteral("project-defaults")); 0306 } 0307 0308 void KdenliveSettingsDialog::initProxyPage() 0309 { 0310 QWidget *p10 = new QWidget; 0311 m_configProxy.setupUi(p10); 0312 m_proxyProfiles = new EncodingProfilesChooser(p10, EncodingProfilesManager::ProxyClips, true, QStringLiteral("proxy_profile")); 0313 m_configProxy.proxy_profile_box->addWidget(m_proxyProfiles); 0314 addPage(p10, i18n("Proxy Clips"), QStringLiteral("zoom-out")); 0315 connect(m_configProxy.kcfg_generateproxy, &QAbstractButton::toggled, m_configProxy.kcfg_proxyminsize, &QWidget::setEnabled); 0316 m_configProxy.kcfg_proxyminsize->setEnabled(KdenliveSettings::generateproxy()); 0317 connect(m_configProxy.kcfg_generateimageproxy, &QAbstractButton::toggled, m_configProxy.kcfg_proxyimageminsize, &QWidget::setEnabled); 0318 m_configProxy.kcfg_proxyimageminsize->setEnabled(KdenliveSettings::generateimageproxy()); 0319 loadExternalProxyProfiles(); 0320 } 0321 0322 void KdenliveSettingsDialog::initEnviromentPage() 0323 { 0324 QWidget *p2 = new QWidget; 0325 m_configEnv.setupUi(p2); 0326 m_configEnv.mltpathurl->setMode(KFile::Directory); 0327 m_configEnv.mltpathurl->lineEdit()->setObjectName(QStringLiteral("kcfg_mltpath")); 0328 m_configEnv.rendererpathurl->lineEdit()->setObjectName(QStringLiteral("kcfg_meltpath")); 0329 m_configEnv.ffmpegurl->lineEdit()->setObjectName(QStringLiteral("kcfg_ffmpegpath")); 0330 m_configEnv.ffplayurl->lineEdit()->setObjectName(QStringLiteral("kcfg_ffplaypath")); 0331 m_configEnv.ffprobeurl->lineEdit()->setObjectName(QStringLiteral("kcfg_ffprobepath")); 0332 m_configEnv.mediainfourl->lineEdit()->setObjectName(QStringLiteral("kcfg_mediainfopath")); 0333 m_configEnv.tmppathurl->setMode(KFile::Directory); 0334 m_configEnv.tmppathurl->lineEdit()->setObjectName(QStringLiteral("kcfg_currenttmpfolder")); 0335 m_configEnv.capturefolderurl->setMode(KFile::Directory); 0336 m_configEnv.capturefolderurl->lineEdit()->setObjectName(QStringLiteral("kcfg_capturefolder")); 0337 m_configEnv.capturefolderurl->setEnabled(KdenliveSettings::capturetoprojectfolder() == 2); 0338 m_configEnv.kcfg_capturetoprojectfolder->setItemText(0, i18n("Use default folder: %1", QStandardPaths::writableLocation(QStandardPaths::MoviesLocation))); 0339 if (KdenliveSettings::customprojectfolder()) { 0340 m_configEnv.kcfg_capturetoprojectfolder->setItemText(1, i18n("Always use project folder: %1", KdenliveSettings::defaultprojectfolder())); 0341 } else { 0342 m_configEnv.kcfg_capturetoprojectfolder->setItemText(1, i18n("Always use active project folder")); 0343 } 0344 connect(m_configEnv.kcfg_capturetoprojectfolder, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 0345 &KdenliveSettingsDialog::slotEnableCaptureFolder); 0346 0347 // Library folder 0348 m_configEnv.libraryfolderurl->setMode(KFile::Directory); 0349 m_configEnv.libraryfolderurl->lineEdit()->setObjectName(QStringLiteral("kcfg_libraryfolder")); 0350 m_configEnv.libraryfolderurl->setEnabled(!KdenliveSettings::librarytodefaultfolder()); 0351 m_configEnv.libraryfolderurl->setPlaceholderText(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/library")); 0352 m_configEnv.kcfg_librarytodefaultfolder->setToolTip(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/library")); 0353 connect(m_configEnv.kcfg_librarytodefaultfolder, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEnableLibraryFolder); 0354 0355 m_configEnv.kcfg_proxythreads->setMaximum(qMax(1, QThread::idealThreadCount() - 1)); 0356 0357 // Script rendering files folder 0358 m_configEnv.videofolderurl->setMode(KFile::Directory); 0359 m_configEnv.videofolderurl->lineEdit()->setObjectName(QStringLiteral("kcfg_videofolder")); 0360 m_configEnv.videofolderurl->setEnabled(KdenliveSettings::videotodefaultfolder() == 2); 0361 m_configEnv.videofolderurl->setPlaceholderText(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)); 0362 m_configEnv.kcfg_videotodefaultfolder->setItemText(0, i18n("Use default folder: %1", QStandardPaths::writableLocation(QStandardPaths::MoviesLocation))); 0363 if (KdenliveSettings::customprojectfolder()) { 0364 m_configEnv.kcfg_videotodefaultfolder->setItemText(1, i18n("Always use project folder: %1", KdenliveSettings::defaultprojectfolder())); 0365 } else { 0366 m_configEnv.kcfg_videotodefaultfolder->setItemText(1, i18n("Always use active project folder")); 0367 } 0368 connect(m_configEnv.kcfg_videotodefaultfolder, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KdenliveSettingsDialog::slotEnableVideoFolder); 0369 0370 // Mime types 0371 QStringList mimes = ClipCreationDialog::getExtensions(); 0372 std::sort(mimes.begin(), mimes.end()); 0373 m_configEnv.supportedmimes->setPlainText(mimes.join(QLatin1Char(' '))); 0374 0375 connect(m_configEnv.kp_image, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditImageApplication); 0376 connect(m_configEnv.kp_audio, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditAudioApplication); 0377 connect(m_configEnv.kp_anim, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditGlaxnimateApplication); 0378 0379 m_pageEnv = addPage(p2, i18n("Environment"), QStringLiteral("application-x-executable-script")); 0380 } 0381 0382 void KdenliveSettingsDialog::initCapturePage() 0383 { 0384 // Remove ffmpeg tab, unused 0385 m_configCapture.tabWidget->removeTab(0); 0386 m_configCapture.label->setVisible(false); 0387 m_configCapture.kcfg_defaultcapture->setVisible(false); 0388 // m_configCapture.tabWidget->removeTab(2); 0389 #ifdef USE_V4L 0390 0391 // Video 4 Linux device detection 0392 for (int i = 0; i < 10; ++i) { 0393 QString path = QStringLiteral("/dev/video") + QString::number(i); 0394 if (QFile::exists(path)) { 0395 QStringList deviceInfo = V4lCaptureHandler::getDeviceName(path); 0396 if (!deviceInfo.isEmpty()) { 0397 m_configCapture.kcfg_detectedv4ldevices->addItem(deviceInfo.at(0), path); 0398 m_configCapture.kcfg_detectedv4ldevices->setItemData(m_configCapture.kcfg_detectedv4ldevices->count() - 1, deviceInfo.at(1), Qt::UserRole + 1); 0399 } 0400 } 0401 } 0402 connect(m_configCapture.kcfg_detectedv4ldevices, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0403 &KdenliveSettingsDialog::slotUpdatev4lDevice); 0404 connect(m_configCapture.kcfg_v4l_format, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0405 &KdenliveSettingsDialog::slotUpdatev4lCaptureProfile); 0406 connect(m_configCapture.config_v4l, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditVideo4LinuxProfile); 0407 0408 slotUpdatev4lDevice(); 0409 #endif 0410 0411 m_configCapture.tabWidget->setCurrentIndex(KdenliveSettings::defaultcapture()); 0412 #ifdef Q_WS_MAC 0413 m_configCapture.tabWidget->setEnabled(false); 0414 m_configCapture.kcfg_defaultcapture->setEnabled(false); 0415 m_configCapture.label->setText(i18n("Capture is not yet available on Mac OS X.")); 0416 #endif 0417 0418 connect(m_configCapture.kcfg_grab_capture_type, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0419 &KdenliveSettingsDialog::slotUpdateGrabRegionStatus); 0420 0421 slotUpdateGrabRegionStatus(); 0422 0423 // audio capture channels 0424 m_configCapture.audiocapturechannels->clear(); 0425 m_configCapture.audiocapturechannels->addItem(i18n("Mono (1 channel)"), 1); 0426 m_configCapture.audiocapturechannels->addItem(i18n("Stereo (2 channels)"), 2); 0427 0428 int channelsIndex = m_configCapture.audiocapturechannels->findData(KdenliveSettings::audiocapturechannels()); 0429 m_configCapture.audiocapturechannels->setCurrentIndex(qMax(channelsIndex, 0)); 0430 connect(m_configCapture.audiocapturechannels, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [&]() { updateButtons(); }); 0431 // audio capture sample rate 0432 m_configCapture.audiocapturesamplerate->clear(); 0433 m_configCapture.audiocapturesamplerate->addItem(i18n("44100 Hz"), 44100); 0434 m_configCapture.audiocapturesamplerate->addItem(i18n("48000 Hz"), 48000); 0435 0436 int sampleRateIndex = m_configCapture.audiocapturesamplerate->findData(KdenliveSettings::audiocapturesamplerate()); 0437 m_configCapture.audiocapturesamplerate->setCurrentIndex(qMax(sampleRateIndex, 0)); 0438 connect(m_configCapture.audiocapturesamplerate, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [&]() { updateButtons(); }); 0439 0440 m_configCapture.labelNoAudioDevices->setVisible(false); 0441 0442 initAudioRecDevice(); 0443 getBlackMagicDeviceList(m_configCapture.kcfg_decklink_capturedevice); 0444 } 0445 0446 void KdenliveSettingsDialog::initJogShuttlePage() 0447 { 0448 QWidget *p6 = new QWidget; 0449 m_configShuttle.setupUi(p6); 0450 #ifdef USE_JOGSHUTTLE 0451 connect(m_configShuttle.kcfg_enableshuttle, &QCheckBox::stateChanged, this, &KdenliveSettingsDialog::slotCheckShuttle); 0452 connect(m_configShuttle.shuttledevicelist, SIGNAL(activated(int)), this, SLOT(slotUpdateShuttleDevice(int))); 0453 connect(m_configShuttle.toolBtnReload, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotReloadShuttleDevices); 0454 0455 slotCheckShuttle(static_cast<int>(KdenliveSettings::enableshuttle())); 0456 m_configShuttle.shuttledisabled->hide(); 0457 0458 // Store the button pointers into an array for easier handling them in the other functions. 0459 // TODO: impl enumerator or live with cut and paste :-))) 0460 setupJogshuttleBtns(KdenliveSettings::shuttledevice()); 0461 #if false 0462 m_shuttle_buttons.push_back(m_configShuttle.shuttle1); 0463 m_shuttle_buttons.push_back(m_configShuttle.shuttle2); 0464 m_shuttle_buttons.push_back(m_configShuttle.shuttle3); 0465 m_shuttle_buttons.push_back(m_configShuttle.shuttle4); 0466 m_shuttle_buttons.push_back(m_configShuttle.shuttle5); 0467 m_shuttle_buttons.push_back(m_configShuttle.shuttle6); 0468 m_shuttle_buttons.push_back(m_configShuttle.shuttle7); 0469 m_shuttle_buttons.push_back(m_configShuttle.shuttle8); 0470 m_shuttle_buttons.push_back(m_configShuttle.shuttle9); 0471 m_shuttle_buttons.push_back(m_configShuttle.shuttle10); 0472 m_shuttle_buttons.push_back(m_configShuttle.shuttle11); 0473 m_shuttle_buttons.push_back(m_configShuttle.shuttle12); 0474 m_shuttle_buttons.push_back(m_configShuttle.shuttle13); 0475 m_shuttle_buttons.push_back(m_configShuttle.shuttle14); 0476 m_shuttle_buttons.push_back(m_configShuttle.shuttle15); 0477 #endif 0478 0479 #else /* ! USE_JOGSHUTTLE */ 0480 m_configShuttle.kcfg_enableshuttle->hide(); 0481 m_configShuttle.kcfg_enableshuttle->setDisabled(true); 0482 #endif /* USE_JOGSHUTTLE */ 0483 m_pageJog = addPage(p6, i18n("JogShuttle"), QStringLiteral("dialog-input-devices")); 0484 } 0485 0486 void KdenliveSettingsDialog::initTranscodePage() 0487 { 0488 QWidget *p8 = new QWidget; 0489 m_configTranscode.setupUi(p8); 0490 m_pageTranscode = addPage(p8, i18n("Transcode"), QStringLiteral("edit-copy")); 0491 0492 connect(m_configTranscode.button_add, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotAddTranscode); 0493 connect(m_configTranscode.button_delete, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotDeleteTranscode); 0494 connect(m_configTranscode.profiles_list, &QListWidget::itemChanged, this, &KdenliveSettingsDialog::slotDialogModified); 0495 connect(m_configTranscode.profiles_list, &QListWidget::currentRowChanged, this, &KdenliveSettingsDialog::slotSetTranscodeProfile); 0496 connect(m_configTranscode.profile_description, &QLineEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate); 0497 connect(m_configTranscode.profile_extension, &QLineEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate); 0498 connect(m_configTranscode.profile_parameters, &QPlainTextEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate); 0499 connect(m_configTranscode.profile_audioonly, &QCheckBox::stateChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate); 0500 0501 connect(m_configTranscode.button_update, &QAbstractButton::pressed, this, &KdenliveSettingsDialog::slotUpdateTranscodingProfile); 0502 0503 m_configTranscode.profile_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5); 0504 loadTranscodeProfiles(); 0505 } 0506 0507 void KdenliveSettingsDialog::initSdlPage(bool gpuAllowed) 0508 { 0509 connect(m_configSdl.reload_blackmagic, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotReloadBlackMagic); 0510 // m_configSdl.kcfg_openglmonitors->setHidden(true); 0511 connect(m_configSdl.fullscreen_monitor, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0512 &KdenliveSettingsDialog::slotDialogModified); 0513 connect(m_configSdl.kcfg_audio_driver, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0514 &KdenliveSettingsDialog::slotCheckAlsaDriver); 0515 connect(m_configSdl.kcfg_audio_backend, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0516 &KdenliveSettingsDialog::slotCheckAudioBackend); 0517 0518 // enable GPU accel only if Movit is found 0519 m_configSdl.kcfg_gpu_accel->setEnabled(gpuAllowed); 0520 m_configSdl.kcfg_gpu_accel->setToolTip(i18n("GPU processing needs MLT compiled with Movit and Rtaudio modules")); 0521 if (!getBlackMagicOutputDeviceList(m_configSdl.kcfg_blackmagic_output_device)) { 0522 // No blackmagic card found 0523 m_configSdl.kcfg_external_display->setEnabled(false); 0524 } 0525 } 0526 0527 bool KdenliveSettingsDialog::getBlackMagicOutputDeviceList(QComboBox *devicelist, bool force) 0528 { 0529 if (!force && !KdenliveSettings::decklink_device_found()) { 0530 return false; 0531 } 0532 Mlt::Profile profile; 0533 Mlt::Consumer bm(profile, "decklink"); 0534 int found_devices = 0; 0535 if (bm.is_valid()) { 0536 bm.set("list_devices", 1); 0537 found_devices = bm.get_int("devices"); 0538 } else { 0539 KdenliveSettings::setDecklink_device_found(false); 0540 } 0541 if (found_devices <= 0) { 0542 devicelist->setEnabled(false); 0543 return false; 0544 } 0545 KdenliveSettings::setDecklink_device_found(true); 0546 for (int i = 0; i < found_devices; ++i) { 0547 char *tmp = qstrdup(QStringLiteral("device.%1").arg(i).toUtf8().constData()); 0548 devicelist->addItem(bm.get(tmp)); 0549 delete[] tmp; 0550 } 0551 devicelist->addItem(QStringLiteral("test")); 0552 return true; 0553 } 0554 0555 void KdenliveSettingsDialog::setupJogshuttleBtns(const QString &device) 0556 { 0557 QList<QComboBox *> list; 0558 QList<QLabel *> list1; 0559 0560 list << m_configShuttle.shuttle1; 0561 list << m_configShuttle.shuttle2; 0562 list << m_configShuttle.shuttle3; 0563 list << m_configShuttle.shuttle4; 0564 list << m_configShuttle.shuttle5; 0565 list << m_configShuttle.shuttle6; 0566 list << m_configShuttle.shuttle7; 0567 list << m_configShuttle.shuttle8; 0568 list << m_configShuttle.shuttle9; 0569 list << m_configShuttle.shuttle10; 0570 list << m_configShuttle.shuttle11; 0571 list << m_configShuttle.shuttle12; 0572 list << m_configShuttle.shuttle13; 0573 list << m_configShuttle.shuttle14; 0574 list << m_configShuttle.shuttle15; 0575 0576 list1 << m_configShuttle.label_2; // #1 0577 list1 << m_configShuttle.label_4; // #2 0578 list1 << m_configShuttle.label_3; // #3 0579 list1 << m_configShuttle.label_7; // #4 0580 list1 << m_configShuttle.label_5; // #5 0581 list1 << m_configShuttle.label_6; // #6 0582 list1 << m_configShuttle.label_8; // #7 0583 list1 << m_configShuttle.label_9; // #8 0584 list1 << m_configShuttle.label_10; // #9 0585 list1 << m_configShuttle.label_11; // #10 0586 list1 << m_configShuttle.label_12; // #11 0587 list1 << m_configShuttle.label_13; // #12 0588 list1 << m_configShuttle.label_14; // #13 0589 list1 << m_configShuttle.label_15; // #14 0590 list1 << m_configShuttle.label_16; // #15 0591 0592 for (int i = 0; i < list.count(); ++i) { 0593 list[i]->hide(); 0594 list1[i]->hide(); 0595 } 0596 #ifdef USE_JOGSHUTTLE 0597 if (!m_configShuttle.kcfg_enableshuttle->isChecked()) { 0598 return; 0599 } 0600 int keysCount = JogShuttle::keysCount(device); 0601 0602 for (int i = 0; i < keysCount; ++i) { 0603 m_shuttle_buttons.push_back(list[i]); 0604 list[i]->show(); 0605 list1[i]->show(); 0606 } 0607 0608 // populate the buttons with the current configuration. The items are sorted 0609 // according to the user-selected language, so they do not appear in random order. 0610 QMap<QString, QString> mappable_actions(m_mappable_actions); 0611 QList<QString> action_names = mappable_actions.keys(); 0612 QList<QString>::Iterator iter = action_names.begin(); 0613 // qCDebug(KDENLIVE_LOG) << "::::::::::::::::"; 0614 while (iter != action_names.end()) { 0615 // qCDebug(KDENLIVE_LOG) << *iter; 0616 ++iter; 0617 } 0618 0619 // qCDebug(KDENLIVE_LOG) << "::::::::::::::::"; 0620 0621 std::sort(action_names.begin(), action_names.end()); 0622 iter = action_names.begin(); 0623 while (iter != action_names.end()) { 0624 // qCDebug(KDENLIVE_LOG) << *iter; 0625 ++iter; 0626 } 0627 // qCDebug(KDENLIVE_LOG) << "::::::::::::::::"; 0628 0629 // Here we need to compute the action_id -> index-in-action_names. We iterate over the 0630 // action_names, as the sorting may depend on the user-language. 0631 QStringList actions_map = JogShuttleConfig::actionMap(KdenliveSettings::shuttlebuttons()); 0632 QMap<QString, int> action_pos; 0633 for (const QString &action_id : qAsConst(actions_map)) { 0634 // This loop find out at what index is the string that would map to the action_id. 0635 for (int i = 0; i < action_names.size(); ++i) { 0636 if (mappable_actions[action_names.at(i)] == action_id) { 0637 action_pos[action_id] = i; 0638 break; 0639 } 0640 } 0641 } 0642 0643 int i = 0; 0644 for (QComboBox *button : qAsConst(m_shuttle_buttons)) { 0645 button->addItems(action_names); 0646 connect(button, SIGNAL(activated(int)), this, SLOT(slotShuttleModified())); 0647 ++i; 0648 if (i < actions_map.size()) { 0649 button->setCurrentIndex(action_pos[actions_map[i]]); 0650 } 0651 } 0652 #else 0653 Q_UNUSED(device) 0654 #endif 0655 } 0656 0657 KdenliveSettingsDialog::~KdenliveSettingsDialog() = default; 0658 0659 void KdenliveSettingsDialog::slotUpdateGrabRegionStatus() 0660 { 0661 m_configCapture.region_group->setHidden(m_configCapture.kcfg_grab_capture_type->currentIndex() != 1); 0662 } 0663 0664 void KdenliveSettingsDialog::slotEnableCaptureFolder(int ix) 0665 { 0666 m_configEnv.capturefolderurl->setEnabled(ix == 2); 0667 } 0668 0669 void KdenliveSettingsDialog::slotEnableLibraryFolder() 0670 { 0671 m_configEnv.libraryfolderurl->setEnabled(!m_configEnv.kcfg_librarytodefaultfolder->isChecked()); 0672 } 0673 0674 void KdenliveSettingsDialog::slotEnableVideoFolder(int ix) 0675 { 0676 m_configEnv.videofolderurl->setEnabled(ix == 2); 0677 } 0678 0679 void KdenliveSettingsDialog::initDevices() 0680 { 0681 // Fill audio drivers 0682 m_configSdl.kcfg_audio_driver->addItem(i18n("Automatic"), QString()); 0683 #if defined(Q_OS_WIN) 0684 // TODO: i18n 0685 m_configSdl.kcfg_audio_driver->addItem("DirectSound", "directsound"); 0686 m_configSdl.kcfg_audio_driver->addItem("WinMM", "winmm"); 0687 m_configSdl.kcfg_audio_driver->addItem("Wasapi", "wasapi"); 0688 #elif !defined(Q_WS_MAC) 0689 m_configSdl.kcfg_audio_driver->addItem(i18n("ALSA"), "alsa"); 0690 m_configSdl.kcfg_audio_driver->addItem(i18n("PulseAudio"), "pulseaudio"); 0691 m_configSdl.kcfg_audio_driver->addItem(i18n("PipeWire"), "pipewire"); 0692 m_configSdl.kcfg_audio_driver->addItem(i18n("OSS"), "dsp"); 0693 // m_configSdl.kcfg_audio_driver->addItem(i18n("OSS with DMA access"), "dma"); 0694 m_configSdl.kcfg_audio_driver->addItem(i18n("Esound Daemon"), "esd"); 0695 m_configSdl.kcfg_audio_driver->addItem(i18n("ARTS Daemon"), "artsc"); 0696 #endif 0697 0698 if (!KdenliveSettings::audiodrivername().isEmpty()) 0699 for (int i = 1; i < m_configSdl.kcfg_audio_driver->count(); ++i) { 0700 if (m_configSdl.kcfg_audio_driver->itemData(i).toString() == KdenliveSettings::audiodrivername()) { 0701 m_configSdl.kcfg_audio_driver->setCurrentIndex(i); 0702 KdenliveSettings::setAudio_driver(uint(i)); 0703 } 0704 } 0705 0706 // Fill the list of audio playback / recording devices 0707 m_configSdl.kcfg_audio_device->addItem(i18n("Default"), QString()); 0708 m_configCapture.kcfg_v4l_alsadevice->addItem(i18n("Default"), "default"); 0709 if (!QStandardPaths::findExecutable(QStringLiteral("aplay")).isEmpty()) { 0710 m_readProcess.setOutputChannelMode(KProcess::OnlyStdoutChannel); 0711 m_readProcess.setProgram(QStringLiteral("aplay"), QStringList() << QStringLiteral("-l")); 0712 connect(&m_readProcess, &KProcess::readyReadStandardOutput, this, &KdenliveSettingsDialog::slotReadAudioDevices); 0713 m_readProcess.execute(5000); 0714 } else { 0715 // If aplay is not installed on the system, parse the /proc/asound/pcm file 0716 QFile file(QStringLiteral("/proc/asound/pcm")); 0717 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { 0718 QTextStream stream(&file); 0719 QString line = stream.readLine(); 0720 QString deviceId; 0721 while (!line.isNull()) { 0722 if (line.contains(QStringLiteral("playback"))) { 0723 deviceId = line.section(QLatin1Char(':'), 0, 0); 0724 m_configSdl.kcfg_audio_device->addItem(line.section(QLatin1Char(':'), 1, 1), QStringLiteral("plughw:%1,%2") 0725 .arg(deviceId.section(QLatin1Char('-'), 0, 0).toInt()) 0726 .arg(deviceId.section(QLatin1Char('-'), 1, 1).toInt())); 0727 } 0728 if (line.contains(QStringLiteral("capture"))) { 0729 deviceId = line.section(QLatin1Char(':'), 0, 0); 0730 m_configCapture.kcfg_v4l_alsadevice->addItem( 0731 line.section(QLatin1Char(':'), 1, 1).simplified(), 0732 QStringLiteral("hw:%1,%2").arg(deviceId.section(QLatin1Char('-'), 0, 0).toInt()).arg(deviceId.section(QLatin1Char('-'), 1, 1).toInt())); 0733 } 0734 line = stream.readLine(); 0735 } 0736 file.close(); 0737 } else { 0738 qCDebug(KDENLIVE_LOG) << " / / / /CANNOT READ PCM"; 0739 } 0740 } 0741 0742 // Add pulseaudio capture option 0743 m_configCapture.kcfg_v4l_alsadevice->addItem(i18n("PulseAudio"), "pulse"); 0744 0745 if (!KdenliveSettings::audiodevicename().isEmpty()) { 0746 // Select correct alsa device 0747 int ix = m_configSdl.kcfg_audio_device->findData(KdenliveSettings::audiodevicename()); 0748 m_configSdl.kcfg_audio_device->setCurrentIndex(ix); 0749 KdenliveSettings::setAudio_device(ix); 0750 } 0751 0752 if (!KdenliveSettings::v4l_alsadevicename().isEmpty()) { 0753 // Select correct alsa device 0754 int ix = m_configCapture.kcfg_v4l_alsadevice->findData(KdenliveSettings::v4l_alsadevicename()); 0755 m_configCapture.kcfg_v4l_alsadevice->setCurrentIndex(ix); 0756 KdenliveSettings::setV4l_alsadevice(ix); 0757 } 0758 0759 m_configSdl.kcfg_audio_backend->addItem(i18n("SDL"), KdenliveSettings::sdlAudioBackend()); 0760 if (KdenliveSettings::consumerslist().isEmpty()) { 0761 Mlt::Properties *consumers = pCore->getMltRepository()->consumers(); 0762 QStringList consumersItemList; 0763 consumersItemList.reserve(consumers->count()); 0764 for (int i = 0; i < consumers->count(); ++i) { 0765 consumersItemList << consumers->get_name(i); 0766 } 0767 delete consumers; 0768 KdenliveSettings::setConsumerslist(consumersItemList); 0769 } 0770 if (KdenliveSettings::consumerslist().contains("rtaudio")) { 0771 m_configSdl.kcfg_audio_backend->addItem(i18n("RtAudio"), "rtaudio"); 0772 } 0773 0774 if (!KdenliveSettings::audiobackend().isEmpty()) { 0775 int ix = m_configSdl.kcfg_audio_backend->findData(KdenliveSettings::audiobackend()); 0776 m_configSdl.kcfg_audio_backend->setCurrentIndex(ix); 0777 KdenliveSettings::setAudio_backend(ix); 0778 } 0779 slotCheckAudioBackend(); 0780 0781 // Fill monitors data 0782 fillMonitorData(); 0783 connect(qApp, &QApplication::screenAdded, this, &KdenliveSettingsDialog::fillMonitorData); 0784 connect(qApp, &QApplication::screenRemoved, this, &KdenliveSettingsDialog::fillMonitorData); 0785 loadCurrentV4lProfileInfo(); 0786 } 0787 0788 void KdenliveSettingsDialog::fillMonitorData() 0789 { 0790 QSignalBlocker bk(m_configSdl.fullscreen_monitor); 0791 m_configSdl.fullscreen_monitor->clear(); 0792 m_configSdl.fullscreen_monitor->addItem(i18n("auto")); 0793 int ix = 0; 0794 for (const QScreen *screen : qApp->screens()) { 0795 QString screenName = screen->name().isEmpty() ? i18n("Monitor %1", ix + 1) : QString("%1: %2").arg(ix + 1).arg(screen->name()); 0796 if (!screen->model().isEmpty()) { 0797 screenName.append(QString(" - %1").arg(screen->model())); 0798 } 0799 if (!screen->manufacturer().isEmpty()) { 0800 screenName.append(QString(" (%1)").arg(screen->manufacturer())); 0801 } 0802 m_configSdl.fullscreen_monitor->addItem(screenName, QString("%1:%2").arg(QString::number(ix), screen->serialNumber())); 0803 ix++; 0804 } 0805 if (!KdenliveSettings::fullscreen_monitor().isEmpty()) { 0806 int ix = m_configSdl.fullscreen_monitor->findData(KdenliveSettings::fullscreen_monitor()); 0807 if (ix > -1) { 0808 m_configSdl.fullscreen_monitor->setCurrentIndex(ix); 0809 } else { 0810 // Monitor not found, reset 0811 m_configSdl.fullscreen_monitor->setCurrentIndex(0); 0812 KdenliveSettings::setFullscreen_monitor(QString()); 0813 } 0814 } 0815 } 0816 0817 void KdenliveSettingsDialog::slotReadAudioDevices() 0818 { 0819 QString result = QString(m_readProcess.readAllStandardOutput()); 0820 // qCDebug(KDENLIVE_LOG) << "// / / / / / READING APLAY: "; 0821 // qCDebug(KDENLIVE_LOG) << result; 0822 const QStringList lines = result.split(QLatin1Char('\n')); 0823 for (const QString &devicestr : lines) { 0824 ////qCDebug(KDENLIVE_LOG) << "// READING LINE: " << data; 0825 if (!devicestr.startsWith(QLatin1Char(' ')) && devicestr.count(QLatin1Char(':')) > 1) { 0826 QString card = devicestr.section(QLatin1Char(':'), 0, 0).section(QLatin1Char(' '), -1); 0827 QString device = devicestr.section(QLatin1Char(':'), 1, 1).section(QLatin1Char(' '), -1); 0828 m_configSdl.kcfg_audio_device->addItem(devicestr.section(QLatin1Char(':'), -1).simplified(), QStringLiteral("plughw:%1,%2").arg(card, device)); 0829 m_configCapture.kcfg_v4l_alsadevice->addItem(devicestr.section(QLatin1Char(':'), -1).simplified(), QStringLiteral("hw:%1,%2").arg(card, device)); 0830 } 0831 } 0832 } 0833 0834 void KdenliveSettingsDialog::showPage(Kdenlive::ConfigPage page, int option) 0835 { 0836 switch (page) { 0837 case Kdenlive::PageMisc: 0838 setCurrentPage(m_pageMisc); 0839 break; 0840 case Kdenlive::PageEnv: 0841 setCurrentPage(m_pageEnv); 0842 break; 0843 case Kdenlive::PageTimeline: 0844 setCurrentPage(m_pageTimeline); 0845 break; 0846 case Kdenlive::PageTools: 0847 setCurrentPage(m_pageTools); 0848 break; 0849 case Kdenlive::PageCapture: 0850 setCurrentPage(m_pageCapture); 0851 m_configCapture.tabWidget->setCurrentIndex(option); 0852 break; 0853 case Kdenlive::PageJogShuttle: 0854 setCurrentPage(m_pageJog); 0855 break; 0856 case Kdenlive::PagePlayback: 0857 setCurrentPage(m_pagePlay); 0858 break; 0859 case Kdenlive::PageTranscode: 0860 setCurrentPage(m_pageTranscode); 0861 break; 0862 case Kdenlive::PageProjectDefaults: 0863 setCurrentPage(m_pageProject); 0864 break; 0865 case Kdenlive::PageColorsGuides: 0866 setCurrentPage(m_pageColors); 0867 break; 0868 case Kdenlive::PageSpeech: 0869 checkSpeechDependencies(); 0870 setCurrentPage(m_pageSpeech); 0871 break; 0872 default: 0873 setCurrentPage(m_pageMisc); 0874 } 0875 } 0876 0877 void KdenliveSettingsDialog::slotEditAudioApplication() 0878 { 0879 QUrl url = KUrlRequesterDialog::getUrl(QUrl::fromLocalFile(KdenliveSettings::defaultaudioapp()), this, i18n("Enter path to the audio editing application")); 0880 if (!url.isEmpty()) { 0881 m_configEnv.kcfg_defaultaudioapp->setText(url.toLocalFile()); 0882 } 0883 } 0884 0885 void KdenliveSettingsDialog::slotEditImageApplication() 0886 { 0887 QUrl url = KUrlRequesterDialog::getUrl(QUrl::fromLocalFile(KdenliveSettings::defaultimageapp()), this, i18n("Enter path to the image editing application")); 0888 if (!url.isEmpty()) { 0889 m_configEnv.kcfg_defaultimageapp->setText(url.toLocalFile()); 0890 } 0891 } 0892 0893 void KdenliveSettingsDialog::slotEditGlaxnimateApplication() 0894 { 0895 QUrl url = KUrlRequesterDialog::getUrl(QUrl::fromLocalFile(KdenliveSettings::glaxnimatePath()), this, i18n("Enter path to the Glaxnimate application")); 0896 if (!url.isEmpty()) { 0897 m_configEnv.kcfg_glaxnimatePath->setText(url.toLocalFile()); 0898 } 0899 } 0900 0901 void KdenliveSettingsDialog::slotCheckShuttle(int state) 0902 { 0903 #ifdef USE_JOGSHUTTLE 0904 m_configShuttle.config_group->setEnabled(state != Qt::Unchecked); 0905 m_configShuttle.shuttledevicelist->clear(); 0906 0907 QStringList devNames = KdenliveSettings::shuttledevicenames(); 0908 QStringList devPaths = KdenliveSettings::shuttledevicepaths(); 0909 0910 if (devNames.count() != devPaths.count()) { 0911 return; 0912 } 0913 for (int i = 0; i < devNames.count(); ++i) { 0914 m_configShuttle.shuttledevicelist->addItem(devNames.at(i), devPaths.at(i)); 0915 } 0916 if (state != Qt::Unchecked) { 0917 setupJogshuttleBtns(m_configShuttle.shuttledevicelist->itemData(m_configShuttle.shuttledevicelist->currentIndex()).toString()); 0918 } 0919 #else 0920 Q_UNUSED(state) 0921 #endif /* USE_JOGSHUTTLE */ 0922 } 0923 0924 void KdenliveSettingsDialog::slotUpdateShuttleDevice(int ix) 0925 { 0926 #ifdef USE_JOGSHUTTLE 0927 QString device = m_configShuttle.shuttledevicelist->itemData(ix).toString(); 0928 // KdenliveSettings::setShuttledevice(device); 0929 setupJogshuttleBtns(device); 0930 m_configShuttle.kcfg_shuttledevice->setText(device); 0931 #else 0932 Q_UNUSED(ix) 0933 #endif /* USE_JOGSHUTTLE */ 0934 } 0935 0936 void KdenliveSettingsDialog::updateWidgets() 0937 { 0938 // Revert widgets to last saved state (for example when user pressed "Cancel") 0939 // //qCDebug(KDENLIVE_LOG) << "// // // KCONFIG Revert called"; 0940 #ifdef USE_JOGSHUTTLE 0941 // revert jog shuttle device 0942 if (m_configShuttle.shuttledevicelist->count() > 0) { 0943 for (int i = 0; i < m_configShuttle.shuttledevicelist->count(); ++i) { 0944 if (m_configShuttle.shuttledevicelist->itemData(i) == KdenliveSettings::shuttledevice()) { 0945 m_configShuttle.shuttledevicelist->setCurrentIndex(i); 0946 break; 0947 } 0948 } 0949 } 0950 0951 // Revert jog shuttle buttons 0952 QList<QString> action_names = m_mappable_actions.keys(); 0953 std::sort(action_names.begin(), action_names.end()); 0954 QStringList actions_map = JogShuttleConfig::actionMap(KdenliveSettings::shuttlebuttons()); 0955 QMap<QString, int> action_pos; 0956 for (const QString &action_id : qAsConst(actions_map)) { 0957 // This loop find out at what index is the string that would map to the action_id. 0958 for (int i = 0; i < action_names.size(); ++i) { 0959 if (m_mappable_actions[action_names[i]] == action_id) { 0960 action_pos[action_id] = i; 0961 break; 0962 } 0963 } 0964 } 0965 int i = 0; 0966 for (QComboBox *button : qAsConst(m_shuttle_buttons)) { 0967 ++i; 0968 if (i < actions_map.size()) { 0969 button->setCurrentIndex(action_pos[actions_map[i]]); 0970 } 0971 } 0972 #endif /* USE_JOGSHUTTLE */ 0973 } 0974 0975 void KdenliveSettingsDialog::accept() 0976 { 0977 if (m_pw->selectedProfile().isEmpty()) { 0978 KMessageBox::error(this, i18n("Please select a video profile")); 0979 return; 0980 } 0981 KConfigDialog::accept(); 0982 } 0983 0984 void KdenliveSettingsDialog::updateExternalApps() 0985 { 0986 m_configEnv.kcfg_defaultimageapp->setText(KdenliveSettings::defaultimageapp()); 0987 m_configEnv.kcfg_defaultaudioapp->setText(KdenliveSettings::defaultaudioapp()); 0988 m_configEnv.kcfg_glaxnimatePath->setText(KdenliveSettings::glaxnimatePath()); 0989 } 0990 0991 void KdenliveSettingsDialog::updateSettings() 0992 { 0993 // Save changes to settings (for example when user pressed "Apply" or "Ok") 0994 // //qCDebug(KDENLIVE_LOG) << "// // // KCONFIG UPDATE called"; 0995 if (m_pw->selectedProfile().isEmpty()) { 0996 KMessageBox::error(this, i18n("Please select a video profile")); 0997 return; 0998 } 0999 KdenliveSettings::setDefault_profile(m_pw->selectedProfile()); 1000 1001 if (m_configEnv.ffmpegurl->text().isEmpty()) { 1002 QString infos; 1003 QString warnings; 1004 QString errors; 1005 Wizard::slotCheckPrograms(infos, warnings, errors); 1006 m_configEnv.ffmpegurl->setText(KdenliveSettings::ffmpegpath()); 1007 m_configEnv.ffplayurl->setText(KdenliveSettings::ffplaypath()); 1008 m_configEnv.ffprobeurl->setText(KdenliveSettings::ffprobepath()); 1009 } 1010 if (m_configEnv.mediainfourl->text().isEmpty()) { 1011 m_configEnv.mediainfourl->setText(KdenliveSettings::mediainfopath()); 1012 } 1013 1014 if (m_configTimeline.kcfg_trackheight->value() == 0) { 1015 QFont ft = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont); 1016 // Default unit for timeline.qml objects size 1017 int baseUnit = qMax(28, qRound(QFontInfo(ft).pixelSize() * 1.8)); 1018 int trackHeight = qMax(50, int(2.2 * baseUnit + 6)); 1019 m_configTimeline.kcfg_trackheight->setValue(trackHeight); 1020 } else if (m_configTimeline.kcfg_trackheight->value() != KdenliveSettings::trackheight()) { 1021 QFont ft = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont); 1022 // Default unit for timeline.qml objects size 1023 int baseUnit = qMax(28, qRound(QFontInfo(ft).pixelSize() * 1.8)); 1024 if (m_configTimeline.kcfg_trackheight->value() < baseUnit) { 1025 m_configTimeline.kcfg_trackheight->setValue(baseUnit); 1026 } 1027 } 1028 1029 bool resetConsumer = false; 1030 bool fullReset = false; 1031 bool updateLibrary = false; 1032 1033 // Guide categories 1034 const QStringList updatedGuides = m_guidesCategories->updatedGuides(); 1035 if (KdenliveSettings::guidesCategories() != updatedGuides) { 1036 KdenliveSettings::setGuidesCategories(updatedGuides); 1037 } 1038 1039 /*if (m_configShuttle.shuttledevicelist->count() > 0) { 1040 QString device = m_configShuttle.shuttledevicelist->itemData(m_configShuttle.shuttledevicelist->currentIndex()).toString(); 1041 if (device != KdenliveSettings::shuttledevice()) KdenliveSettings::setShuttledevice(device); 1042 }*/ 1043 m_tlPreviewProfiles->hideMessage(); 1044 if (m_configProject.projecturl->url().toLocalFile() != KdenliveSettings::defaultprojectfolder()) { 1045 KdenliveSettings::setDefaultprojectfolder(m_configProject.projecturl->url().toLocalFile()); 1046 if (!KdenliveSettings::sameprojectfolder()) { 1047 m_configEnv.kcfg_videotodefaultfolder->setItemText(1, i18n("Always use project folder: %1", KdenliveSettings::defaultprojectfolder())); 1048 m_configEnv.kcfg_capturetoprojectfolder->setItemText(1, i18n("Always use project folder: %1", KdenliveSettings::defaultprojectfolder())); 1049 } 1050 } 1051 1052 if (m_configProject.kcfg_customprojectfolder->isChecked() != KdenliveSettings::customprojectfolder()) { 1053 if (KdenliveSettings::customprojectfolder()) { 1054 m_configEnv.kcfg_videotodefaultfolder->setItemText(1, i18n("Always use active project folder")); 1055 m_configEnv.kcfg_capturetoprojectfolder->setItemText(1, i18n("Always use active project folder")); 1056 } else { 1057 m_configEnv.kcfg_videotodefaultfolder->setItemText(1, i18n("Always use project folder: %1", KdenliveSettings::defaultprojectfolder())); 1058 m_configEnv.kcfg_capturetoprojectfolder->setItemText(1, i18n("Always use project folder: %1", KdenliveSettings::defaultprojectfolder())); 1059 } 1060 } 1061 1062 if (m_configSdl.fullscreen_monitor->currentData().toString() != KdenliveSettings::fullscreen_monitor()) { 1063 KdenliveSettings::setFullscreen_monitor(m_configSdl.fullscreen_monitor->currentData().toString()); 1064 } 1065 1066 if (m_configEnv.capturefolderurl->url().toLocalFile() != KdenliveSettings::capturefolder()) { 1067 KdenliveSettings::setCapturefolder(m_configEnv.capturefolderurl->url().toLocalFile()); 1068 } 1069 1070 // Library default folder 1071 if (m_configEnv.kcfg_librarytodefaultfolder->isChecked() != KdenliveSettings::librarytodefaultfolder()) { 1072 KdenliveSettings::setLibrarytodefaultfolder(m_configEnv.kcfg_librarytodefaultfolder->isChecked()); 1073 updateLibrary = true; 1074 } 1075 1076 if (m_configEnv.libraryfolderurl->url().toLocalFile() != KdenliveSettings::libraryfolder()) { 1077 KdenliveSettings::setLibraryfolder(m_configEnv.libraryfolderurl->url().toLocalFile()); 1078 if (!KdenliveSettings::librarytodefaultfolder()) { 1079 updateLibrary = true; 1080 } 1081 } 1082 1083 if (m_configCapture.kcfg_v4l_format->currentIndex() != int(KdenliveSettings::v4l_format())) { 1084 saveCurrentV4lProfile(); 1085 KdenliveSettings::setV4l_format(0); 1086 } 1087 1088 // Check if screengrab is fullscreen 1089 if (m_configCapture.kcfg_grab_capture_type->currentIndex() != KdenliveSettings::grab_capture_type()) { 1090 KdenliveSettings::setGrab_capture_type(m_configCapture.kcfg_grab_capture_type->currentIndex()); 1091 Q_EMIT updateFullScreenGrab(); 1092 } 1093 1094 // Check audio capture changes 1095 if (KdenliveSettings::audiocapturechannels() != m_configCapture.audiocapturechannels->currentData().toInt() || 1096 KdenliveSettings::audiocapturevolume() != m_configCapture.kcfg_audiocapturevolume->value() || 1097 KdenliveSettings::audiocapturesamplerate() != m_configCapture.audiocapturesamplerate->currentData().toInt()) { 1098 KdenliveSettings::setAudiocapturechannels(m_configCapture.audiocapturechannels->currentData().toInt()); 1099 KdenliveSettings::setAudiocapturevolume(m_configCapture.kcfg_audiocapturevolume->value()); 1100 KdenliveSettings::setAudiocapturesamplerate(m_configCapture.audiocapturesamplerate->currentData().toInt()); 1101 Q_EMIT resetAudioMonitoring(); 1102 } 1103 1104 // Check encoding profiles 1105 // FFmpeg 1106 QString string = m_v4lProfiles->currentParams(); 1107 if (string != KdenliveSettings::v4l_parameters()) { 1108 KdenliveSettings::setV4l_parameters(string); 1109 } 1110 string = m_v4lProfiles->currentExtension(); 1111 if (string != KdenliveSettings::v4l_extension()) { 1112 KdenliveSettings::setV4l_extension(string); 1113 } 1114 1115 // screengrab 1116 string = m_grabProfiles->currentParams(); 1117 if (string != KdenliveSettings::grab_parameters()) { 1118 KdenliveSettings::setGrab_parameters(string); 1119 } 1120 string = m_grabProfiles->currentExtension(); 1121 if (string != KdenliveSettings::grab_extension()) { 1122 KdenliveSettings::setGrab_extension(string); 1123 } 1124 1125 // decklink 1126 string = m_decklinkProfiles->currentParams(); 1127 if (string != KdenliveSettings::decklink_parameters()) { 1128 KdenliveSettings::setDecklink_parameters(string); 1129 } 1130 string = m_decklinkProfiles->currentExtension(); 1131 if (string != KdenliveSettings::decklink_extension()) { 1132 KdenliveSettings::setDecklink_extension(string); 1133 } 1134 1135 // proxies 1136 string = m_proxyProfiles->currentParams(); 1137 if (string != KdenliveSettings::proxyparams()) { 1138 KdenliveSettings::setProxyparams(string); 1139 } 1140 string = m_proxyProfiles->currentExtension(); 1141 if (string != KdenliveSettings::proxyextension()) { 1142 KdenliveSettings::setProxyextension(string); 1143 } 1144 1145 // external proxies 1146 QString profilestr = m_configProxy.kcfg_external_proxy_profile->currentData().toString(); 1147 if (!profilestr.isEmpty() && (profilestr != KdenliveSettings::externalProxyProfile())) { 1148 KdenliveSettings::setExternalProxyProfile(profilestr); 1149 } 1150 1151 // timeline preview 1152 string = m_tlPreviewProfiles->currentParams(); 1153 if (string != KdenliveSettings::previewparams()) { 1154 KdenliveSettings::setPreviewparams(string); 1155 } 1156 string = m_tlPreviewProfiles->currentExtension(); 1157 if (string != KdenliveSettings::previewextension()) { 1158 KdenliveSettings::setPreviewextension(string); 1159 } 1160 1161 if (updateLibrary) { 1162 Q_EMIT updateLibraryFolder(); 1163 } 1164 1165 QString value = m_configCapture.kcfg_v4l_alsadevice->currentData().toString(); 1166 if (value != KdenliveSettings::v4l_alsadevicename()) { 1167 KdenliveSettings::setV4l_alsadevicename(value); 1168 } 1169 1170 if (m_configSdl.kcfg_external_display->isChecked() != KdenliveSettings::external_display()) { 1171 KdenliveSettings::setExternal_display(m_configSdl.kcfg_external_display->isChecked()); 1172 resetConsumer = true; 1173 fullReset = true; 1174 } else if (KdenliveSettings::external_display() && 1175 KdenliveSettings::blackmagic_output_device() != m_configSdl.kcfg_blackmagic_output_device->currentIndex()) { 1176 resetConsumer = true; 1177 fullReset = true; 1178 } 1179 1180 value = m_configSdl.kcfg_audio_driver->currentData().toString(); 1181 if (value != KdenliveSettings::audiodrivername()) { 1182 KdenliveSettings::setAudiodrivername(value); 1183 resetConsumer = true; 1184 } 1185 1186 if (value == QLatin1String("alsa")) { 1187 // Audio device setting is only valid for alsa driver 1188 value = m_configSdl.kcfg_audio_device->currentData().toString(); 1189 if (value != KdenliveSettings::audiodevicename()) { 1190 KdenliveSettings::setAudiodevicename(value); 1191 resetConsumer = true; 1192 } 1193 } else if (!KdenliveSettings::audiodevicename().isEmpty()) { 1194 KdenliveSettings::setAudiodevicename(QString()); 1195 resetConsumer = true; 1196 } 1197 1198 value = m_configSdl.kcfg_audio_backend->currentData().toString(); 1199 if (value != KdenliveSettings::audiobackend()) { 1200 KdenliveSettings::setAudiobackend(value); 1201 resetConsumer = true; 1202 fullReset = true; 1203 } 1204 1205 if (m_configColors.kcfg_window_background->color() != KdenliveSettings::window_background()) { 1206 KdenliveSettings::setWindow_background(m_configColors.kcfg_window_background->color()); 1207 Q_EMIT updateMonitorBg(); 1208 } 1209 1210 if (m_configColors.kcfg_thumbColor1->color() != KdenliveSettings::thumbColor1() || 1211 m_configColors.kcfg_thumbColor2->color() != KdenliveSettings::thumbColor2()) { 1212 KdenliveSettings::setThumbColor1(m_configColors.kcfg_thumbColor1->color()); 1213 KdenliveSettings::setThumbColor2(m_configColors.kcfg_thumbColor2->color()); 1214 Q_EMIT pCore->window()->getCurrentTimeline()->controller()->colorsChanged(); 1215 pCore->getMonitor(Kdenlive::ClipMonitor)->refreshAudioThumbs(); 1216 } 1217 1218 if (m_configColors.kcfg_overlayColor->color() != KdenliveSettings::overlayColor()) { 1219 KdenliveSettings::setOverlayColor(m_configColors.kcfg_overlayColor->color()); 1220 pCore->getMonitor(Kdenlive::ProjectMonitor)->getControllerProxy()->colorsChanged(); 1221 pCore->getMonitor(Kdenlive::ClipMonitor)->getControllerProxy()->colorsChanged(); 1222 } 1223 1224 if (m_configMisc.kcfg_tabposition->currentIndex() != KdenliveSettings::tabposition()) { 1225 KdenliveSettings::setTabposition(m_configMisc.kcfg_tabposition->currentIndex()); 1226 } 1227 1228 if (m_configTimeline.kcfg_displayallchannels->isChecked() != KdenliveSettings::displayallchannels()) { 1229 KdenliveSettings::setDisplayallchannels(m_configTimeline.kcfg_displayallchannels->isChecked()); 1230 Q_EMIT audioThumbFormatChanged(); 1231 pCore->getMonitor(Kdenlive::ClipMonitor)->refreshAudioThumbs(); 1232 } 1233 1234 if (m_modified) { 1235 // The transcoding profiles were modified, save. 1236 m_modified = false; 1237 saveTranscodeProfiles(); 1238 } 1239 1240 #ifdef USE_JOGSHUTTLE 1241 m_shuttleModified = false; 1242 1243 QStringList actions; 1244 actions << QStringLiteral("monitor_pause"); // the Job rest position action. 1245 for (QComboBox *button : qAsConst(m_shuttle_buttons)) { 1246 actions << m_mappable_actions[button->currentText()]; 1247 } 1248 QString maps = JogShuttleConfig::actionMap(actions); 1249 // fprintf(stderr, "Shuttle config: %s\n", JogShuttleConfig::actionMap(actions).toLatin1().constData()); 1250 if (KdenliveSettings::shuttlebuttons() != maps) { 1251 KdenliveSettings::setShuttlebuttons(maps); 1252 } 1253 #endif 1254 1255 bool restart = false; 1256 if (m_configSdl.kcfg_gpu_accel->isChecked() != KdenliveSettings::gpu_accel()) { 1257 // GPU setting was changed, we need to restart Kdenlive or everything will be corrupted 1258 if (KMessageBox::warningContinueCancel(this, i18n("Kdenlive must be restarted to change this setting")) == KMessageBox::Continue) { 1259 restart = true; 1260 } else { 1261 m_configSdl.kcfg_gpu_accel->setChecked(KdenliveSettings::gpu_accel()); 1262 } 1263 } 1264 1265 if (m_configTimeline.kcfg_trackheight->value() != KdenliveSettings::trackheight()) { 1266 KdenliveSettings::setTrackheight(m_configTimeline.kcfg_trackheight->value()); 1267 Q_EMIT resetView(); 1268 } 1269 1270 if (m_configTimeline.kcfg_autoscroll->isChecked() != KdenliveSettings::autoscroll()) { 1271 KdenliveSettings::setAutoscroll(m_configTimeline.kcfg_autoscroll->isChecked()); 1272 Q_EMIT pCore->autoScrollChanged(); 1273 } 1274 1275 if (m_configTimeline.kcfg_pauseonseek->isChecked() != KdenliveSettings::pauseonseek()) { 1276 KdenliveSettings::setPauseonseek(m_configTimeline.kcfg_pauseonseek->isChecked()); 1277 } 1278 1279 if (m_configTimeline.kcfg_seekonaddeffect->isChecked() != KdenliveSettings::seekonaddeffect()) { 1280 KdenliveSettings::setSeekonaddeffect(m_configTimeline.kcfg_seekonaddeffect->isChecked()); 1281 } 1282 1283 if (m_configTimeline.kcfg_scrollvertically->isChecked() != KdenliveSettings::scrollvertically()) { 1284 KdenliveSettings::setScrollvertically(m_configTimeline.kcfg_scrollvertically->isChecked()); 1285 Q_EMIT pCore->window()->getCurrentTimeline()->controller()->scrollVerticallyChanged(); 1286 } 1287 1288 // Speech 1289 switch (m_configSpeech.speech_stack->currentIndex()) { 1290 case 1: 1291 if (KdenliveSettings::speechEngine() != QLatin1String("whisper")) { 1292 KdenliveSettings::setSpeechEngine(QStringLiteral("whisper")); 1293 Q_EMIT pCore->speechEngineChanged(); 1294 } 1295 break; 1296 default: 1297 if (!KdenliveSettings::speechEngine().isEmpty()) { 1298 KdenliveSettings::setSpeechEngine(QString()); 1299 Q_EMIT pCore->speechEngineChanged(); 1300 } 1301 break; 1302 } 1303 1304 if (m_configSpeech.combo_wr_lang->currentData().toString() != KdenliveSettings::whisperLanguage()) { 1305 KdenliveSettings::setWhisperLanguage(m_configSpeech.combo_wr_lang->currentData().toString()); 1306 } 1307 if (m_configSpeech.combo_wr_model->currentData().toString() != KdenliveSettings::whisperModel()) { 1308 KdenliveSettings::setWhisperModel(m_configSpeech.combo_wr_model->currentData().toString()); 1309 } 1310 if (m_configSpeech.combo_wr_device->currentData().toString() != KdenliveSettings::whisperDevice()) { 1311 KdenliveSettings::setWhisperDevice(m_configSpeech.combo_wr_device->currentData().toString()); 1312 } 1313 1314 // Mimes 1315 if (m_configEnv.kcfg_addedExtensions->text() != KdenliveSettings::addedExtensions()) { 1316 // Update list 1317 KdenliveSettings::setAddedExtensions(m_configEnv.kcfg_addedExtensions->text()); 1318 QStringList mimes = ClipCreationDialog::getExtensions(); 1319 std::sort(mimes.begin(), mimes.end()); 1320 m_configEnv.supportedmimes->setPlainText(mimes.join(QLatin1Char(' '))); 1321 } 1322 1323 // proxy/transcode max concurrent jobs 1324 if (m_configEnv.kcfg_proxythreads->value() != KdenliveSettings::proxythreads()) { 1325 KdenliveSettings::setProxythreads(m_configEnv.kcfg_proxythreads->value()); 1326 pCore->taskManager.updateConcurrency(); 1327 } 1328 1329 KConfigDialog::settingsChangedSlot(); 1330 // KConfigDialog::updateSettings(); 1331 if (resetConsumer) { 1332 Q_EMIT doResetConsumer(fullReset); 1333 } 1334 if (restart) { 1335 Q_EMIT restartKdenlive(); 1336 } 1337 Q_EMIT checkTabPosition(); 1338 1339 // remembering Config dialog size 1340 KSharedConfigPtr config = KSharedConfig::openConfig(); 1341 KConfigGroup settingsGroup(config, "settings"); 1342 settingsGroup.writeEntry("dialogSize", QVariant(size())); 1343 } 1344 1345 void KdenliveSettingsDialog::slotCheckAlsaDriver() 1346 { 1347 QString value = m_configSdl.kcfg_audio_driver->currentData().toString(); 1348 m_configSdl.kcfg_audio_device->setEnabled(value == QLatin1String("alsa")); 1349 } 1350 1351 void KdenliveSettingsDialog::slotCheckAudioBackend() 1352 { 1353 bool isSdl = m_configSdl.kcfg_audio_backend->currentData().toString().startsWith(QLatin1String("sdl")); 1354 m_configSdl.label_audio_driver->setEnabled(isSdl); 1355 m_configSdl.kcfg_audio_driver->setEnabled(isSdl); 1356 m_configSdl.label_audio_device->setEnabled(isSdl); 1357 m_configSdl.kcfg_audio_device->setEnabled(isSdl); 1358 } 1359 1360 void KdenliveSettingsDialog::loadTranscodeProfiles() 1361 { 1362 KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("kdenlivetranscodingrc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation); 1363 KConfigGroup transConfig(config, "Transcoding"); 1364 // read the entries 1365 m_configTranscode.profiles_list->blockSignals(true); 1366 m_configTranscode.profiles_list->clear(); 1367 QMap<QString, QString> profiles = transConfig.entryMap(); 1368 QMapIterator<QString, QString> i(profiles); 1369 while (i.hasNext()) { 1370 i.next(); 1371 auto *item = new QListWidgetItem(i.key()); 1372 QString profilestr = i.value(); 1373 if (profilestr.contains(QLatin1Char(';'))) { 1374 item->setToolTip(profilestr.section(QLatin1Char(';'), 1, 1)); 1375 } 1376 item->setData(Qt::UserRole, profilestr); 1377 m_configTranscode.profiles_list->addItem(item); 1378 // item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable); 1379 } 1380 m_configTranscode.profiles_list->blockSignals(false); 1381 m_configTranscode.profiles_list->setCurrentRow(0); 1382 } 1383 1384 void KdenliveSettingsDialog::saveTranscodeProfiles() 1385 { 1386 QString transcodeFile = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/kdenlivetranscodingrc"); 1387 KSharedConfigPtr config = KSharedConfig::openConfig(transcodeFile); 1388 KConfigGroup transConfig(config, "Transcoding"); 1389 // read the entries 1390 transConfig.deleteGroup(); 1391 int max = m_configTranscode.profiles_list->count(); 1392 for (int i = 0; i < max; ++i) { 1393 QListWidgetItem *item = m_configTranscode.profiles_list->item(i); 1394 transConfig.writeEntry(item->text(), item->data(Qt::UserRole).toString()); 1395 } 1396 config->sync(); 1397 } 1398 1399 void KdenliveSettingsDialog::slotAddTranscode() 1400 { 1401 bool ok; 1402 QString presetName = 1403 QInputDialog::getText(this, i18nc("@title:window", "Enter Preset Name"), i18n("Enter the name of this preset:"), QLineEdit::Normal, QString(), &ok); 1404 if (!ok) return; 1405 if (!m_configTranscode.profiles_list->findItems(presetName, Qt::MatchExactly).isEmpty()) { 1406 KMessageBox::error(this, i18n("A profile with that name already exists")); 1407 return; 1408 } 1409 QListWidgetItem *item = new QListWidgetItem(presetName); 1410 QString profilestr = m_configTranscode.profile_parameters->toPlainText(); 1411 profilestr.append(" %1." + m_configTranscode.profile_extension->text()); 1412 profilestr.append(';'); 1413 if (!m_configTranscode.profile_description->text().isEmpty()) { 1414 profilestr.append(m_configTranscode.profile_description->text()); 1415 } 1416 if (m_configTranscode.profile_audioonly->isChecked()) { 1417 profilestr.append(";audio"); 1418 } 1419 item->setData(Qt::UserRole, profilestr); 1420 m_configTranscode.profiles_list->addItem(item); 1421 m_configTranscode.profiles_list->setCurrentItem(item); 1422 slotDialogModified(); 1423 } 1424 1425 void KdenliveSettingsDialog::slotUpdateTranscodingProfile() 1426 { 1427 QListWidgetItem *item = m_configTranscode.profiles_list->currentItem(); 1428 if (!item) { 1429 return; 1430 } 1431 m_configTranscode.button_update->setEnabled(false); 1432 QString profilestr = m_configTranscode.profile_parameters->toPlainText(); 1433 profilestr.append(" %1." + m_configTranscode.profile_extension->text()); 1434 profilestr.append(';'); 1435 if (!m_configTranscode.profile_description->text().isEmpty()) { 1436 profilestr.append(m_configTranscode.profile_description->text()); 1437 } 1438 if (m_configTranscode.profile_audioonly->isChecked()) { 1439 profilestr.append(QStringLiteral(";audio")); 1440 } 1441 item->setData(Qt::UserRole, profilestr); 1442 slotDialogModified(); 1443 } 1444 1445 void KdenliveSettingsDialog::slotDeleteTranscode() 1446 { 1447 QListWidgetItem *item = m_configTranscode.profiles_list->currentItem(); 1448 if (item == nullptr) { 1449 return; 1450 } 1451 delete item; 1452 slotDialogModified(); 1453 } 1454 1455 void KdenliveSettingsDialog::slotEnableTranscodeUpdate() 1456 { 1457 if (!m_configTranscode.profile_box->isEnabled()) { 1458 return; 1459 } 1460 bool allow = true; 1461 if (m_configTranscode.profile_extension->text().isEmpty()) { 1462 allow = false; 1463 } 1464 m_configTranscode.button_update->setEnabled(allow); 1465 } 1466 1467 void KdenliveSettingsDialog::slotSetTranscodeProfile() 1468 { 1469 m_configTranscode.profile_box->setEnabled(false); 1470 m_configTranscode.button_update->setEnabled(false); 1471 m_configTranscode.profile_description->clear(); 1472 m_configTranscode.profile_extension->clear(); 1473 m_configTranscode.profile_parameters->clear(); 1474 m_configTranscode.profile_audioonly->setChecked(false); 1475 QListWidgetItem *item = m_configTranscode.profiles_list->currentItem(); 1476 if (!item) { 1477 return; 1478 } 1479 QString profilestr = item->data(Qt::UserRole).toString(); 1480 if (profilestr.contains(QLatin1Char(';'))) { 1481 m_configTranscode.profile_description->setText(profilestr.section(QLatin1Char(';'), 1, 1)); 1482 if (profilestr.section(QLatin1Char(';'), 2, 2) == QLatin1String("audio")) { 1483 m_configTranscode.profile_audioonly->setChecked(true); 1484 } 1485 profilestr = profilestr.section(QLatin1Char(';'), 0, 0).simplified(); 1486 } 1487 m_configTranscode.profile_extension->setText(profilestr.section(QLatin1Char('.'), -1)); 1488 m_configTranscode.profile_parameters->setPlainText(profilestr.section(QLatin1Char(' '), 0, -2)); 1489 m_configTranscode.profile_box->setEnabled(true); 1490 } 1491 1492 void KdenliveSettingsDialog::slotShuttleModified() 1493 { 1494 #ifdef USE_JOGSHUTTLE 1495 QStringList actions; 1496 actions << QStringLiteral("monitor_pause"); // the Job rest position action. 1497 for (QComboBox *button : qAsConst(m_shuttle_buttons)) { 1498 actions << m_mappable_actions[button->currentText()]; 1499 } 1500 QString maps = JogShuttleConfig::actionMap(actions); 1501 m_shuttleModified = KdenliveSettings::shuttlebuttons() != maps; 1502 #endif 1503 KConfigDialog::updateButtons(); 1504 } 1505 1506 void KdenliveSettingsDialog::slotDialogModified() 1507 { 1508 m_modified = true; 1509 KConfigDialog::updateButtons(); 1510 } 1511 1512 // virtual 1513 bool KdenliveSettingsDialog::hasChanged() 1514 { 1515 if (m_modified || m_shuttleModified) { 1516 return true; 1517 } 1518 if (KdenliveSettings::audiocapturechannels() != m_configCapture.audiocapturechannels->currentData().toInt()) { 1519 return true; 1520 } 1521 if (KdenliveSettings::audiocapturesamplerate() != m_configCapture.audiocapturesamplerate->currentData().toInt()) { 1522 return true; 1523 } 1524 return KConfigDialog::hasChanged(); 1525 } 1526 1527 void KdenliveSettingsDialog::slotUpdatev4lDevice() 1528 { 1529 QString device = m_configCapture.kcfg_detectedv4ldevices->itemData(m_configCapture.kcfg_detectedv4ldevices->currentIndex()).toString(); 1530 if (!device.isEmpty()) { 1531 m_configCapture.kcfg_video4vdevice->setText(device); 1532 } 1533 QString info = m_configCapture.kcfg_detectedv4ldevices->itemData(m_configCapture.kcfg_detectedv4ldevices->currentIndex(), Qt::UserRole + 1).toString(); 1534 1535 m_configCapture.kcfg_v4l_format->blockSignals(true); 1536 m_configCapture.kcfg_v4l_format->clear(); 1537 1538 QString vl4ProfilePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/video4linux"); 1539 if (QFile::exists(vl4ProfilePath)) { 1540 m_configCapture.kcfg_v4l_format->addItem(i18n("Current settings")); 1541 } 1542 1543 QStringList pixelformats = info.split('>', Qt::SkipEmptyParts); 1544 QString itemSize; 1545 QStringList itemRates; 1546 for (int i = 0; i < pixelformats.count(); ++i) { 1547 QString format = pixelformats.at(i).section(QLatin1Char(':'), 0, 0); 1548 QStringList sizes = pixelformats.at(i).split(':', Qt::SkipEmptyParts); 1549 sizes.takeFirst(); 1550 for (int j = 0; j < sizes.count(); ++j) { 1551 itemSize = sizes.at(j).section(QLatin1Char('='), 0, 0); 1552 itemRates = sizes.at(j).section(QLatin1Char('='), 1, 1).split(QLatin1Char(','), Qt::SkipEmptyParts); 1553 for (int k = 0; k < itemRates.count(); ++k) { 1554 m_configCapture.kcfg_v4l_format->addItem( 1555 QLatin1Char('[') + format + QStringLiteral("] ") + itemSize + QStringLiteral(" (") + itemRates.at(k) + QLatin1Char(')'), 1556 QStringList() << format << itemSize.section('x', 0, 0) << itemSize.section('x', 1, 1) << itemRates.at(k).section(QLatin1Char('/'), 0, 0) 1557 << itemRates.at(k).section(QLatin1Char('/'), 1, 1)); 1558 } 1559 } 1560 } 1561 m_configCapture.kcfg_v4l_format->blockSignals(false); 1562 slotUpdatev4lCaptureProfile(); 1563 } 1564 1565 void KdenliveSettingsDialog::slotUpdatev4lCaptureProfile() 1566 { 1567 QStringList info = m_configCapture.kcfg_v4l_format->itemData(m_configCapture.kcfg_v4l_format->currentIndex(), Qt::UserRole).toStringList(); 1568 if (info.isEmpty()) { 1569 // No auto info, display the current ones 1570 loadCurrentV4lProfileInfo(); 1571 return; 1572 } 1573 m_configCapture.p_size->setText(info.at(1) + QLatin1Char('x') + info.at(2)); 1574 m_configCapture.p_fps->setText(info.at(3) + QLatin1Char('/') + info.at(4)); 1575 m_configCapture.p_aspect->setText(QStringLiteral("1/1")); 1576 m_configCapture.p_display->setText(info.at(1) + QLatin1Char('/') + info.at(2)); 1577 m_configCapture.p_colorspace->setText(ProfileRepository::getColorspaceDescription(601)); 1578 m_configCapture.p_progressive->setText(i18n("Progressive")); 1579 1580 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/")); 1581 if (!dir.exists() || !dir.exists(QStringLiteral("video4linux"))) { 1582 saveCurrentV4lProfile(); 1583 } 1584 } 1585 1586 void KdenliveSettingsDialog::loadCurrentV4lProfileInfo() 1587 { 1588 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/")); 1589 if (!dir.exists()) { 1590 dir.mkpath(QStringLiteral(".")); 1591 } 1592 if (!ProfileRepository::get()->profileExists(dir.absoluteFilePath(QStringLiteral("video4linux")))) { 1593 // No default formats found, build one 1594 std::unique_ptr<ProfileParam> prof(new ProfileParam(pCore->getCurrentProfile().get())); 1595 prof->m_width = 320; 1596 prof->m_height = 200; 1597 prof->m_frame_rate_num = 15; 1598 prof->m_frame_rate_den = 1; 1599 prof->m_display_aspect_num = 4; 1600 prof->m_display_aspect_den = 3; 1601 prof->m_sample_aspect_num = 1; 1602 prof->m_sample_aspect_den = 1; 1603 prof->m_progressive = true; 1604 prof->m_colorspace = 601; 1605 ProfileRepository::get()->saveProfile(prof.get(), dir.absoluteFilePath(QStringLiteral("video4linux"))); 1606 } 1607 auto &prof = ProfileRepository::get()->getProfile(dir.absoluteFilePath(QStringLiteral("video4linux"))); 1608 m_configCapture.p_size->setText(QString::number(prof->width()) + QLatin1Char('x') + QString::number(prof->height())); 1609 m_configCapture.p_fps->setText(QString::number(prof->frame_rate_num()) + QLatin1Char('/') + QString::number(prof->frame_rate_den())); 1610 m_configCapture.p_aspect->setText(QString::number(prof->sample_aspect_num()) + QLatin1Char('/') + QString::number(prof->sample_aspect_den())); 1611 m_configCapture.p_display->setText(QString::number(prof->display_aspect_num()) + QLatin1Char('/') + QString::number(prof->display_aspect_den())); 1612 m_configCapture.p_colorspace->setText(ProfileRepository::getColorspaceDescription(prof->colorspace())); 1613 if (prof->progressive()) { 1614 m_configCapture.p_progressive->setText(i18n("Progressive")); 1615 } 1616 } 1617 1618 void KdenliveSettingsDialog::saveCurrentV4lProfile() 1619 { 1620 std::unique_ptr<ProfileParam> profile(new ProfileParam(pCore->getCurrentProfile().get())); 1621 profile->m_description = QStringLiteral("Video4Linux capture"); 1622 profile->m_colorspace = ProfileRepository::getColorspaceFromDescription(m_configCapture.p_colorspace->text()); 1623 profile->m_width = m_configCapture.p_size->text().section('x', 0, 0).toInt(); 1624 profile->m_height = m_configCapture.p_size->text().section('x', 1, 1).toInt(); 1625 profile->m_sample_aspect_num = m_configCapture.p_aspect->text().section(QLatin1Char('/'), 0, 0).toInt(); 1626 profile->m_sample_aspect_den = m_configCapture.p_aspect->text().section(QLatin1Char('/'), 1, 1).toInt(); 1627 profile->m_display_aspect_num = m_configCapture.p_display->text().section(QLatin1Char('/'), 0, 0).toInt(); 1628 profile->m_display_aspect_den = m_configCapture.p_display->text().section(QLatin1Char('/'), 1, 1).toInt(); 1629 profile->m_frame_rate_num = m_configCapture.p_fps->text().section(QLatin1Char('/'), 0, 0).toInt(); 1630 profile->m_frame_rate_den = m_configCapture.p_fps->text().section(QLatin1Char('/'), 1, 1).toInt(); 1631 profile->m_progressive = m_configCapture.p_progressive->text() == i18n("Progressive"); 1632 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/")); 1633 if (!dir.exists()) { 1634 dir.mkpath(QStringLiteral(".")); 1635 } 1636 ProfileRepository::get()->saveProfile(profile.get(), dir.absoluteFilePath(QStringLiteral("video4linux"))); 1637 } 1638 1639 void KdenliveSettingsDialog::loadExternalProxyProfiles() 1640 { 1641 // load proxy profiles 1642 KConfig conf(QStringLiteral("externalproxies.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation); 1643 KConfigGroup group(&conf, "proxy"); 1644 QMap<QString, QString> values = group.entryMap(); 1645 QMapIterator<QString, QString> k(values); 1646 QString currentItem = KdenliveSettings::externalProxyProfile(); 1647 m_configProxy.kcfg_external_proxy_profile->blockSignals(true); 1648 m_configProxy.kcfg_external_proxy_profile->clear(); 1649 while (k.hasNext()) { 1650 k.next(); 1651 if (!k.key().isEmpty()) { 1652 if (k.value().contains(QLatin1Char(';'))) { 1653 m_configProxy.kcfg_external_proxy_profile->addItem(k.key(), k.value()); 1654 } 1655 } 1656 } 1657 if (!currentItem.isEmpty()) { 1658 m_configProxy.kcfg_external_proxy_profile->setCurrentIndex(m_configProxy.kcfg_external_proxy_profile->findText(currentItem)); 1659 } 1660 m_configProxy.kcfg_external_proxy_profile->blockSignals(false); 1661 } 1662 1663 void KdenliveSettingsDialog::slotEditVideo4LinuxProfile() 1664 { 1665 QString vl4ProfilePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/video4linux"); 1666 QPointer<ProfilesDialog> w = new ProfilesDialog(vl4ProfilePath, true); 1667 if (w->exec() == QDialog::Accepted) { 1668 // save and update profile 1669 loadCurrentV4lProfileInfo(); 1670 } 1671 delete w; 1672 } 1673 1674 void KdenliveSettingsDialog::slotReloadBlackMagic() 1675 { 1676 getBlackMagicDeviceList(m_configCapture.kcfg_decklink_capturedevice, true); 1677 if (!getBlackMagicOutputDeviceList(m_configSdl.kcfg_blackmagic_output_device, true)) { 1678 // No blackmagic card found 1679 m_configSdl.kcfg_external_display->setEnabled(false); 1680 } 1681 m_configSdl.kcfg_external_display->setEnabled(KdenliveSettings::decklink_device_found()); 1682 } 1683 1684 void KdenliveSettingsDialog::checkProfile() 1685 { 1686 m_pw->loadProfile(KdenliveSettings::default_profile().isEmpty() ? pCore->getCurrentProfile()->path() : KdenliveSettings::default_profile()); 1687 } 1688 1689 void KdenliveSettingsDialog::slotReloadShuttleDevices() 1690 { 1691 #ifdef USE_JOGSHUTTLE 1692 QString devDirStr = QStringLiteral("/dev/input/by-id"); 1693 QDir devDir(devDirStr); 1694 if (!devDir.exists()) { 1695 devDirStr = QStringLiteral("/dev/input"); 1696 } 1697 1698 QStringList devNamesList; 1699 QStringList devPathList; 1700 m_configShuttle.shuttledevicelist->clear(); 1701 1702 DeviceMap devMap = JogShuttle::enumerateDevices(devDirStr); 1703 DeviceMapIter iter = devMap.begin(); 1704 while (iter != devMap.end()) { 1705 m_configShuttle.shuttledevicelist->addItem(iter.key(), iter.value()); 1706 devNamesList << iter.key(); 1707 devPathList << iter.value(); 1708 ++iter; 1709 } 1710 KdenliveSettings::setShuttledevicenames(devNamesList); 1711 KdenliveSettings::setShuttledevicepaths(devPathList); 1712 QTimer::singleShot(200, this, [&]() { slotUpdateShuttleDevice(); }); 1713 #endif // USE_JOGSHUTTLE 1714 } 1715 1716 void KdenliveSettingsDialog::initSpeechPage() 1717 { 1718 m_stt = new SpeechToText(SpeechToText::EngineType::EngineVosk, this); 1719 m_sttWhisper = new SpeechToText(SpeechToText::EngineType::EngineWhisper, this); 1720 // Python env info label 1721 PythonDependencyMessage *pythonEnvLabel = new PythonDependencyMessage(this, m_sttWhisper, true); 1722 m_configEnv.message_layout_2->addWidget(pythonEnvLabel); 1723 // Also show VOSK setup messages in the python env page 1724 connect(m_stt, &AbstractPythonInterface::setupMessage, 1725 [pythonEnvLabel](const QString message, int type) { pythonEnvLabel->doShowMessage(message, KMessageWidget::MessageType(type)); }); 1726 connect(m_stt, &AbstractPythonInterface::venvSetupChanged, this, [this]() { 1727 QSignalBlocker bk(m_configEnv.kcfg_usePythonVenv); 1728 m_configEnv.kcfg_usePythonVenv->setChecked(KdenliveSettings::usePythonVenv()); 1729 }); 1730 connect(m_sttWhisper, &AbstractPythonInterface::venvSetupChanged, this, [this]() { 1731 QSignalBlocker bk(m_configEnv.kcfg_usePythonVenv); 1732 m_configEnv.kcfg_usePythonVenv->setChecked(KdenliveSettings::usePythonVenv()); 1733 }); 1734 m_sttWhisper->checkPython(KdenliveSettings::usePythonVenv(), true); 1735 QButtonGroup *speechEngineSelection = new QButtonGroup(this); 1736 speechEngineSelection->addButton(m_configSpeech.engine_vosk); 1737 speechEngineSelection->addButton(m_configSpeech.engine_whisper); 1738 connect(speechEngineSelection, &QButtonGroup::buttonClicked, [this](QAbstractButton *button) { 1739 if (button == m_configSpeech.engine_vosk) { 1740 m_configSpeech.speech_stack->setCurrentIndex(0); 1741 m_stt->checkDependencies(false); 1742 } else if (button == m_configSpeech.engine_whisper) { 1743 m_configSpeech.speech_stack->setCurrentIndex(1); 1744 m_sttWhisper->checkDependencies(false); 1745 } 1746 }); 1747 if (KdenliveSettings::speechEngine() == QLatin1String("whisper")) { 1748 m_configSpeech.engine_whisper->setChecked(true); 1749 m_configSpeech.speech_stack->setCurrentIndex(1); 1750 } else { 1751 m_configSpeech.engine_vosk->setChecked(true); 1752 m_configSpeech.speech_stack->setCurrentIndex(0); 1753 } 1754 // Python setup 1755 m_configEnv.pythonSetupMessage->hide(); 1756 connect(m_sttWhisper, &SpeechToText::gotPythonSize, m_configEnv.label_python_size, &QLabel::setText); 1757 connect(m_configEnv.kcfg_usePythonVenv, &QCheckBox::stateChanged, this, [this](int state) { 1758 if (m_sttWhisper->installInProcess()) { 1759 return; 1760 } 1761 m_configEnv.pythonSetupMessage->setMessageType(KMessageWidget::Information); 1762 if (state == Qt::Checked) { 1763 m_configEnv.pythonSetupMessage->setText(i18n("Setting up virtual environment…")); 1764 } else { 1765 m_configEnv.pythonSetupMessage->setText(i18n("Disabling virtual environment")); 1766 } 1767 m_configEnv.pythonSetupMessage->show(); 1768 qApp->processEvents(); 1769 bool updatePython = m_sttWhisper->checkPython(state == Qt::Checked, true); 1770 if (!updatePython) { 1771 // Setting up venv failed 1772 m_configEnv.kcfg_usePythonVenv->setChecked(false); 1773 } else { 1774 // Venv setting changed, refresh all speech interfaces 1775 m_stt->checkDependencies(true); 1776 m_sttWhisper->checkDependencies(true); 1777 } 1778 m_configEnv.pythonSetupMessage->hide(); 1779 }); 1780 connect(m_configEnv.button_python_delete, &QPushButton::clicked, this, [this]() { 1781 m_configEnv.pythonSetupMessage->setMessageType(KMessageWidget::Information); 1782 m_configEnv.pythonSetupMessage->setText(i18nc("@label:textbox", "Removing the virtual environment folder.")); 1783 m_configEnv.pythonSetupMessage->show(); 1784 if (m_sttWhisper->removePythonVenv()) { 1785 m_configEnv.kcfg_usePythonVenv->setChecked(false); 1786 m_configEnv.pythonSetupMessage->hide(); 1787 m_configEnv.label_python_size->setText(i18nc("@label:textbox", "No python venv found")); 1788 } else { 1789 m_configEnv.pythonSetupMessage->setMessageType(KMessageWidget::Warning); 1790 m_configEnv.pythonSetupMessage->setText(i18nc("@label:textbox", "Failed to remove the virtual environment folder.")); 1791 } 1792 }); 1793 1794 // Whisper 1795 m_configSpeech.combo_wr_device->addItem(i18n("Probing...")); 1796 PythonDependencyMessage *msgWhisper = new PythonDependencyMessage(this, m_sttWhisper); 1797 m_configSpeech.message_layout_wr->addWidget(msgWhisper); 1798 QList<std::pair<QString, QString>> whisperModels = m_sttWhisper->whisperModels(); 1799 for (auto &w : whisperModels) { 1800 m_configSpeech.combo_wr_model->addItem(w.first, w.second); 1801 } 1802 int ix = m_configSpeech.combo_wr_model->findData(KdenliveSettings::whisperModel()); 1803 if (ix > -1) { 1804 m_configSpeech.combo_wr_model->setCurrentIndex(ix); 1805 } 1806 QMap<QString, QString> whisperLanguages = m_sttWhisper->whisperLanguages(); 1807 QMapIterator<QString, QString> j(whisperLanguages); 1808 while (j.hasNext()) { 1809 j.next(); 1810 m_configSpeech.combo_wr_lang->addItem(j.key(), j.value()); 1811 } 1812 ix = m_configSpeech.combo_wr_lang->findData(KdenliveSettings::whisperLanguage()); 1813 if (ix > -1) { 1814 m_configSpeech.combo_wr_lang->setCurrentIndex(ix); 1815 } 1816 m_configSpeech.script_log->hide(); 1817 connect(m_sttWhisper, &SpeechToText::scriptStarted, [this]() { QMetaObject::invokeMethod(m_configSpeech.script_log, "clear"); }); 1818 connect(m_sttWhisper, &SpeechToText::installFeedback, [this](const QString jobData) { 1819 QMetaObject::invokeMethod(m_configSpeech.script_log, "show", Qt::QueuedConnection); 1820 QMetaObject::invokeMethod(m_configSpeech.script_log, "appendPlainText", Q_ARG(QString, jobData)); 1821 }); 1822 connect(m_sttWhisper, &SpeechToText::scriptFinished, [msgWhisper]() { QMetaObject::invokeMethod(msgWhisper, "checkAfterInstall", Qt::QueuedConnection); }); 1823 1824 connect(m_sttWhisper, &SpeechToText::scriptFeedback, [this](const QStringList jobData) { 1825 m_configSpeech.combo_wr_device->clear(); 1826 for (auto &s : jobData) { 1827 if (s.contains(QLatin1Char('#'))) { 1828 m_configSpeech.combo_wr_device->addItem(s.section(QLatin1Char('#'), 1).simplified(), s.section(QLatin1Char('#'), 0, 0).simplified()); 1829 } else { 1830 m_configSpeech.combo_wr_device->addItem(s.simplified(), s.simplified()); 1831 } 1832 } 1833 }); 1834 connect(m_sttWhisper, &SpeechToText::scriptGpuCheckFinished, [this]() { 1835 int ix = m_configSpeech.combo_wr_device->findData(KdenliveSettings::whisperDevice()); 1836 if (ix > -1) { 1837 m_configSpeech.combo_wr_device->setCurrentIndex(ix); 1838 } 1839 }); 1840 connect(m_sttWhisper, &SpeechToText::dependenciesAvailable, this, [&]() { m_sttWhisper->runConcurrentScript(QStringLiteral("checkgpu.py"), {}); }); 1841 1842 // VOSK 1843 m_configSpeech.vosk_folder->setPlaceholderText( 1844 QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("speechmodels"), QStandardPaths::LocateDirectory)); 1845 PythonDependencyMessage *msgVosk = new PythonDependencyMessage(this, m_stt); 1846 m_configSpeech.message_layout->addWidget(msgVosk); 1847 1848 connect(m_stt, &SpeechToText::dependenciesAvailable, this, [&]() { 1849 if (m_speechListWidget->count() == 0) { 1850 doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information); 1851 } 1852 }); 1853 connect(m_stt, &SpeechToText::dependenciesMissing, this, [&](const QStringList &) { m_configSpeech.speech_info->animatedHide(); }); 1854 connect(m_stt, &SpeechToText::scriptStarted, [this]() { QMetaObject::invokeMethod(m_configSpeech.script_log, "clear"); }); 1855 connect(m_stt, &SpeechToText::installFeedback, [this](const QString jobData) { 1856 QMetaObject::invokeMethod(m_configSpeech.script_log, "show", Qt::QueuedConnection); 1857 QMetaObject::invokeMethod(m_configSpeech.script_log, "appendPlainText", Q_ARG(QString, jobData)); 1858 }); 1859 connect(m_stt, &SpeechToText::scriptFinished, [msgVosk]() { QMetaObject::invokeMethod(msgVosk, "checkAfterInstall", Qt::QueuedConnection); }); 1860 1861 m_speechListWidget = new SpeechList(this); 1862 connect(m_speechListWidget, &SpeechList::getDictionary, this, &KdenliveSettingsDialog::getDictionary); 1863 QVBoxLayout *l = new QVBoxLayout(m_configSpeech.list_frame); 1864 l->setContentsMargins(0, 0, 0, 0); 1865 l->addWidget(m_speechListWidget); 1866 m_configSpeech.speech_info->setWordWrap(true); 1867 connect(m_configSpeech.check_config, &QPushButton::clicked, this, &KdenliveSettingsDialog::slotCheckSttConfig); 1868 1869 connect(m_configSpeech.custom_vosk_folder, &QCheckBox::stateChanged, this, [this](int state) { 1870 m_configSpeech.vosk_folder->setEnabled(state != Qt::Unchecked); 1871 if (state == Qt::Unchecked) { 1872 // Clear custom folder 1873 m_configSpeech.vosk_folder->clear(); 1874 KdenliveSettings::setVosk_folder_path(QString()); 1875 slotParseVoskDictionaries(); 1876 } 1877 }); 1878 connect(m_configSpeech.vosk_folder, &KUrlRequester::urlSelected, this, [this](const QUrl &url) { 1879 KdenliveSettings::setVosk_folder_path(url.toLocalFile()); 1880 slotParseVoskDictionaries(); 1881 }); 1882 m_configSpeech.models_url->setText( 1883 i18n("Download speech models from: <a href=\"https://alphacephei.com/vosk/models\">https://alphacephei.com/vosk/models</a>")); 1884 connect(m_configSpeech.models_url, &QLabel::linkActivated, this, [&](const QString &contents) { 1885 qDebug() << "=== LINK CLICKED: " << contents; 1886 auto *job = new KIO::OpenUrlJob(QUrl(contents)); 1887 #if KIO_VERSION >= QT_VERSION_CHECK(5, 98, 0) 1888 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this)); 1889 #else 1890 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this)); 1891 #endif 1892 // methods like setRunExecutables, setSuggestedFilename, setEnableExternalBrowser, setFollowRedirections 1893 // exist in both classes 1894 job->start(); 1895 }); 1896 connect(m_configSpeech.button_add, &QToolButton::clicked, this, [this]() { this->getDictionary(); }); 1897 connect(m_configSpeech.button_delete, &QToolButton::clicked, this, &KdenliveSettingsDialog::removeDictionary); 1898 connect(this, &KdenliveSettingsDialog::parseDictionaries, this, &KdenliveSettingsDialog::slotParseVoskDictionaries); 1899 slotParseVoskDictionaries(); 1900 } 1901 1902 void KdenliveSettingsDialog::slotCheckSttConfig() 1903 { 1904 m_configSpeech.check_config->setEnabled(false); 1905 qApp->processEvents(); 1906 if (m_configSpeech.engine_vosk->isChecked()) { 1907 m_stt->checkDependencies(true); 1908 } else { 1909 m_sttWhisper->checkDependencies(true); 1910 } 1911 // Leave button disabled for 3 seconds so that the user doesn't trigger it again while it is processing 1912 QTimer::singleShot(3000, this, [&]() { m_configSpeech.check_config->setEnabled(true); }); 1913 } 1914 1915 void KdenliveSettingsDialog::doShowSpeechMessage(const QString &message, int messageType) 1916 { 1917 m_configSpeech.speech_info->setMessageType(static_cast<KMessageWidget::MessageType>(messageType)); 1918 m_configSpeech.speech_info->setText(message); 1919 m_configSpeech.speech_info->animatedShow(); 1920 } 1921 1922 void KdenliveSettingsDialog::getDictionary(const QUrl &sourceUrl) 1923 { 1924 QUrl url = KUrlRequesterDialog::getUrl(sourceUrl, this, i18n("Enter url for the new dictionary")); 1925 if (url.isEmpty()) { 1926 return; 1927 } 1928 if (!url.isLocalFile()) { 1929 KIO::FileCopyJob *copyjob = KIO::file_copy(url, QUrl::fromLocalFile(QDir::temp().absoluteFilePath(url.fileName()))); 1930 doShowSpeechMessage(i18n("Downloading model…"), KMessageWidget::Information); 1931 connect(copyjob, &KIO::FileCopyJob::result, this, &KdenliveSettingsDialog::downloadModelFinished); 1932 } else { 1933 processArchive(url.toLocalFile()); 1934 } 1935 } 1936 1937 void KdenliveSettingsDialog::removeDictionary() 1938 { 1939 if (!KdenliveSettings::vosk_folder_path().isEmpty()) { 1940 doShowSpeechMessage(i18n("We do not allow deleting custom folder models, please do it manually."), KMessageWidget::Warning); 1941 return; 1942 } 1943 QString modelDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 1944 QDir dir(modelDirectory); 1945 if (!dir.cd(QStringLiteral("speechmodels"))) { 1946 doShowSpeechMessage(i18n("Cannot access dictionary folder."), KMessageWidget::Warning); 1947 return; 1948 } 1949 1950 if (!m_speechListWidget->currentItem()) { 1951 return; 1952 } 1953 QString currentModel = m_speechListWidget->currentItem()->text(); 1954 if (!currentModel.isEmpty() && dir.cd(currentModel)) { 1955 if (KMessageBox::questionTwoActions(this, i18n("Delete folder:\n%1", dir.absolutePath()), {}, KStandardGuiItem::del(), KStandardGuiItem::cancel()) == 1956 KMessageBox::PrimaryAction) { 1957 // Make sure we don't accidentally delete a folder that is not ours 1958 if (dir.absolutePath().contains(QLatin1String("speechmodels"))) { 1959 dir.removeRecursively(); 1960 slotParseVoskDictionaries(); 1961 } 1962 } 1963 } 1964 } 1965 1966 void KdenliveSettingsDialog::downloadModelFinished(KJob *job) 1967 { 1968 qDebug() << "=== DOWNLOAD FINISHED!!"; 1969 if (job->error() == 0 || job->error() == 112) { 1970 qDebug() << "=== NO ERROR ON DWNLD!!"; 1971 auto *jb = static_cast<KIO::FileCopyJob *>(job); 1972 if (jb) { 1973 qDebug() << "=== JOB FOUND!!"; 1974 QString archiveFile = jb->destUrl().toLocalFile(); 1975 processArchive(archiveFile); 1976 } else { 1977 qDebug() << "=== JOB NOT FOUND!!"; 1978 m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning); 1979 m_configSpeech.speech_info->setText(i18n("Download error")); 1980 } 1981 } else { 1982 qDebug() << "=== GOT JOB ERROR: " << job->error(); 1983 m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning); 1984 m_configSpeech.speech_info->setText(i18n("Download error %1", job->errorString())); 1985 } 1986 } 1987 1988 void KdenliveSettingsDialog::processArchive(const QString &archiveFile) 1989 { 1990 QMimeDatabase db; 1991 QMimeType type = db.mimeTypeForFile(archiveFile); 1992 std::unique_ptr<KArchive> archive; 1993 if (type.inherits(QStringLiteral("application/zip"))) { 1994 archive = std::make_unique<KZip>(archiveFile); 1995 } else { 1996 archive = std::make_unique<KTar>(archiveFile); 1997 } 1998 QString modelDirectory = KdenliveSettings::vosk_folder_path(); 1999 QDir dir; 2000 if (modelDirectory.isEmpty()) { 2001 modelDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 2002 dir = QDir(modelDirectory); 2003 dir.mkdir(QStringLiteral("speechmodels")); 2004 if (!dir.cd(QStringLiteral("speechmodels"))) { 2005 doShowSpeechMessage(i18n("Cannot access dictionary folder."), KMessageWidget::Warning); 2006 return; 2007 } 2008 } else { 2009 dir = QDir(modelDirectory); 2010 } 2011 if (archive->open(QIODevice::ReadOnly)) { 2012 doShowSpeechMessage(i18n("Extracting archive…"), KMessageWidget::Information); 2013 const KArchiveDirectory *archiveDir = archive->directory(); 2014 if (!archiveDir->copyTo(dir.absolutePath())) { 2015 qDebug() << "=== Error extracting archive!!"; 2016 } else { 2017 QFile::remove(archiveFile); 2018 Q_EMIT parseDictionaries(); 2019 doShowSpeechMessage(i18n("New dictionary installed."), KMessageWidget::Positive); 2020 } 2021 } else { 2022 // Test if it is a folder 2023 QDir testDir(archiveFile); 2024 if (testDir.exists()) { 2025 } 2026 qDebug() << "=== CANNOT OPEN ARCHIVE!!"; 2027 } 2028 } 2029 2030 void KdenliveSettingsDialog::slotParseVoskDictionaries() 2031 { 2032 m_speechListWidget->clear(); 2033 QStringList final = m_stt->parseVoskDictionaries(); 2034 m_speechListWidget->addItems(final); 2035 if (!KdenliveSettings::vosk_folder_path().isEmpty()) { 2036 m_configSpeech.custom_vosk_folder->setChecked(true); 2037 m_configSpeech.vosk_folder->setUrl(QUrl::fromLocalFile(KdenliveSettings::vosk_folder_path())); 2038 } 2039 if (!final.isEmpty() && m_stt->missingDependencies().isEmpty()) { 2040 m_configSpeech.speech_info->animatedHide(); 2041 } else if (final.isEmpty()) { 2042 doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information); 2043 } 2044 } 2045 2046 void KdenliveSettingsDialog::showHelp() 2047 { 2048 pCore->window()->appHelpActivated(); 2049 } 2050 2051 void KdenliveSettingsDialog::checkSpeechDependencies() 2052 { 2053 m_sttWhisper->checkDependenciesConcurrently(); 2054 m_stt->checkDependenciesConcurrently(); 2055 }