1 /*
2 SPDX-FileCopyrightText: 2021 Jean-Baptiste Mardelle <jb@kdenlive.org>
3
4 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6
7 #include "kdenlivesettingsdialog.h"
8 #include "clipcreationdialog.h"
9 #include "core.h"
10 #include "dialogs/profilesdialog.h"
11 #include "encodingprofilesdialog.h"
12 #include "kdenlivesettings.h"
13 #include "mainwindow.h"
14 #include "timeline2/view/timelinewidget.h"
15 #include "timeline2/view/timelinecontroller.h"
16 #include "profiles/profilemodel.hpp"
17 #include "profiles/profilerepository.hpp"
18 #include "profilesdialog.h"
19 #include "project/dialogs/profilewidget.h"
20 #include "wizard.h"
21 #include "monitor/monitor.h"
22 #include "doc/kdenlivedoc.h"
23
24 #ifdef USE_V4L
25 #include "capture/v4lcapture.h"
26 #endif
27
28 #include "kdenlive_debug.h"
29 #include "klocalizedstring.h"
30 #include <KIO/DesktopExecParser>
31 #include <kio_version.h>
32
33 #if KIO_VERSION > QT_VERSION_CHECK(5, 70, 0)
34 #include <KIO/OpenUrlJob>
35 #else
36 #include <KRun>
37 #endif
38
39 #include <KUrlRequesterDialog>
40 #include <KArchive>
41 #include <KZip>
42 #include <KTar>
43 #include <KIO/FileCopyJob>
44 #include <KLineEdit>
45 #include <KMessageBox>
46 #include <KOpenWithDialog>
47 #include <KIO/JobUiDelegate>
48 #include <KArchiveDirectory>
49 #include <KService>
50 #include <QAction>
51 #include <QDir>
52 #include <QGuiApplication>
53 #include <QScreen>
54 #include <QSize>
55 #include <QThread>
56 #include <QTimer>
57 #include <QtConcurrent>
58 #include <QRegularExpression>
59 #include <cstdio>
60 #include <cstdlib>
61 #include <fcntl.h>
62 #include <unistd.h>
63
64 #ifdef USE_JOGSHUTTLE
65 #include "jogshuttle/jogaction.h"
66 #include "jogshuttle/jogshuttleconfig.h"
67 #include <QStandardPaths>
68 #include <linux/input.h>
69 #include <memory>
70 #endif
71
SpeechList(QWidget * parent)72 SpeechList::SpeechList(QWidget *parent)
73 : QListWidget(parent)
74 {
75 setAlternatingRowColors(true);
76 setAcceptDrops(true);
77 setDropIndicatorShown(true);
78 viewport()->setAcceptDrops(true);
79 }
80
mimeTypes() const81 QStringList SpeechList::mimeTypes() const
82 {
83 return QStringList() << QStringLiteral("text/uri-list");
84 }
85
dropEvent(QDropEvent * event)86 void SpeechList::dropEvent(QDropEvent *event)
87 {
88 const QMimeData *qMimeData = event->mimeData();
89 if (qMimeData->hasUrls()) {
90 QList<QUrl> urls = qMimeData->urls();
91 if (!urls.isEmpty()) {
92 emit getDictionary(urls.takeFirst());
93 }
94 }
95 }
96
KdenliveSettingsDialog(QMap<QString,QString> mappable_actions,bool gpuAllowed,QWidget * parent)97 KdenliveSettingsDialog::KdenliveSettingsDialog(QMap<QString, QString> mappable_actions, bool gpuAllowed, QWidget *parent)
98 : KConfigDialog(parent, QStringLiteral("settings"), KdenliveSettings::self())
99 , m_modified(false)
100 , m_shuttleModified(false)
101 , m_voskUpdated(false)
102 , m_mappable_actions(std::move(mappable_actions))
103 {
104 KdenliveSettings::setV4l_format(0);
105 QWidget *p1 = new QWidget;
106 m_configMisc.setupUi(p1);
107 m_page1 = addPage(p1, i18n("Misc"));
108 m_page1->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
109
110 m_configMisc.kcfg_use_exiftool->setEnabled(!QStandardPaths::findExecutable(QStringLiteral("exiftool")).isEmpty());
111
112 QRegularExpression reg(R"((\+|-)?\d{2}:\d{2}:\d{2}(:||,)\d{2})");
113 QValidator *validator = new QRegularExpressionValidator(reg, this);
114 m_configMisc.kcfg_color_duration->setInputMask(pCore->timecode().mask());
115 m_configMisc.kcfg_color_duration->setValidator(validator);
116 m_configMisc.kcfg_title_duration->setInputMask(pCore->timecode().mask());
117 m_configMisc.kcfg_title_duration->setValidator(validator);
118 m_configMisc.kcfg_transition_duration->setInputMask(pCore->timecode().mask());
119 m_configMisc.kcfg_transition_duration->setValidator(validator);
120 m_configMisc.kcfg_mix_duration->setInputMask(pCore->timecode().mask());
121 m_configMisc.kcfg_mix_duration->setValidator(validator);
122 m_configMisc.kcfg_image_duration->setInputMask(pCore->timecode().mask());
123 m_configMisc.kcfg_image_duration->setValidator(validator);
124 m_configMisc.kcfg_sequence_duration->setInputMask(pCore->timecode().mask());
125 m_configMisc.kcfg_sequence_duration->setValidator(validator);
126 m_configMisc.kcfg_fade_duration->setInputMask(pCore->timecode().mask());
127 m_configMisc.kcfg_fade_duration->setValidator(validator);
128 m_configMisc.kcfg_subtitle_duration->setInputMask(pCore->timecode().mask());
129 m_configMisc.kcfg_subtitle_duration->setValidator(validator);
130
131 if (!KdenliveSettings::preferredcomposite().isEmpty()) {
132 int ix = m_configMisc.preferredcomposite->findData(KdenliveSettings::preferredcomposite());
133 if (ix > -1) {
134 m_configMisc.preferredcomposite->setCurrentIndex(ix);
135 }
136 }
137 connect(m_configMisc.preferredcomposite, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,[&](){
138 if (m_configMisc.preferredcomposite->currentText() != KdenliveSettings::preferredcomposite()) {
139 KdenliveSettings::setPreferredcomposite(m_configMisc.preferredcomposite->currentText());
140 int mode = pCore->currentDoc()->getDocumentProperty(QStringLiteral("compositing")).toInt();
141 pCore->window()->getMainTimeline()->controller()->switchCompositing(mode);
142 pCore->currentDoc()->setModified();
143 }
144 });
145
146 QWidget *p8 = new QWidget;
147 m_configProject.setupUi(p8);
148 m_page8 = addPage(p8, i18n("Project Defaults"));
149 auto *vbox = new QVBoxLayout;
150 m_pw = new ProfileWidget(this);
151 vbox->addWidget(m_pw);
152 m_configProject.profile_box->setLayout(vbox);
153 m_configProject.profile_box->setTitle(i18n("Select the default profile (preset)"));
154 // Select profile
155 m_pw->loadProfile(KdenliveSettings::default_profile().isEmpty() ? pCore->getCurrentProfile()->path() : KdenliveSettings::default_profile());
156 connect(m_pw, &ProfileWidget::profileChanged, this, &KdenliveSettingsDialog::slotDialogModified);
157 m_page8->setIcon(QIcon::fromTheme(QStringLiteral("project-defaults")));
158 m_configProject.projecturl->setMode(KFile::Directory);
159 m_configProject.projecturl->setUrl(QUrl::fromLocalFile(KdenliveSettings::defaultprojectfolder()));
160 connect(m_configProject.kcfg_customprojectfolder, &QCheckBox::stateChanged, this, [this](int state){
161 m_configProject.kcfg_sameprojectfolder->setEnabled(state == Qt::Unchecked);
162 });
163 connect(m_configProject.kcfg_sameprojectfolder, &QCheckBox::stateChanged, this, [this](int state){
164 m_configProject.kcfg_customprojectfolder->setEnabled(state == Qt::Unchecked);
165 m_configProject.projecturl->setEnabled(state == Qt::Unchecked);
166 });
167 connect(m_configProject.kcfg_videotracks, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this]() {
168 if (m_configProject.kcfg_videotracks->value() + m_configProject.kcfg_audiotracks->value() <= 0) {
169 m_configProject.kcfg_videotracks->setValue(1);
170 }
171 });
172 connect(m_configProject.kcfg_audiotracks, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this] () {
173 if (m_configProject.kcfg_videotracks->value() + m_configProject.kcfg_audiotracks->value() <= 0) {
174 m_configProject.kcfg_audiotracks->setValue(1);
175 }
176 });
177
178 QWidget *p9 = new QWidget;
179 m_configProxy.setupUi(p9);
180 KPageWidgetItem *page9 = addPage(p9, i18n("Proxy Clips"));
181 page9->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out")));
182 connect(m_configProxy.kcfg_generateproxy, &QAbstractButton::toggled, m_configProxy.kcfg_proxyminsize, &QWidget::setEnabled);
183 m_configProxy.kcfg_proxyminsize->setEnabled(KdenliveSettings::generateproxy());
184 connect(m_configProxy.kcfg_generateimageproxy, &QAbstractButton::toggled, m_configProxy.kcfg_proxyimageminsize, &QWidget::setEnabled);
185 m_configProxy.kcfg_proxyimageminsize->setEnabled(KdenliveSettings::generateimageproxy());
186 loadExternalProxyProfiles();
187
188 QWidget *p3 = new QWidget;
189 m_configTimeline.setupUi(p3);
190 m_page3 = addPage(p3, i18n("Timeline"));
191 m_page3->setIcon(QIcon::fromTheme(QStringLiteral("video-display")));
192
193 QWidget *p2 = new QWidget;
194 m_configEnv.setupUi(p2);
195 m_configEnv.mltpathurl->setMode(KFile::Directory);
196 m_configEnv.mltpathurl->lineEdit()->setObjectName(QStringLiteral("kcfg_mltpath"));
197 m_configEnv.rendererpathurl->lineEdit()->setObjectName(QStringLiteral("kcfg_rendererpath"));
198 m_configEnv.ffmpegurl->lineEdit()->setObjectName(QStringLiteral("kcfg_ffmpegpath"));
199 m_configEnv.ffplayurl->lineEdit()->setObjectName(QStringLiteral("kcfg_ffplaypath"));
200 m_configEnv.ffprobeurl->lineEdit()->setObjectName(QStringLiteral("kcfg_ffprobepath"));
201 m_configEnv.mediainfourl->lineEdit()->setObjectName(QStringLiteral("kcfg_mediainfopath"));
202 m_configEnv.tmppathurl->setMode(KFile::Directory);
203 m_configEnv.tmppathurl->lineEdit()->setObjectName(QStringLiteral("kcfg_currenttmpfolder"));
204 m_configEnv.capturefolderurl->setMode(KFile::Directory);
205 m_configEnv.capturefolderurl->lineEdit()->setObjectName(QStringLiteral("kcfg_capturefolder"));
206 m_configEnv.capturefolderurl->setEnabled(!KdenliveSettings::capturetoprojectfolder());
207 connect(m_configEnv.kcfg_capturetoprojectfolder, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEnableCaptureFolder);
208 // Library folder
209 m_configEnv.libraryfolderurl->setMode(KFile::Directory);
210 m_configEnv.libraryfolderurl->lineEdit()->setObjectName(QStringLiteral("kcfg_libraryfolder"));
211 m_configEnv.libraryfolderurl->setEnabled(!KdenliveSettings::librarytodefaultfolder());
212 m_configEnv.libraryfolderurl->setPlaceholderText(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/library"));
213 m_configEnv.kcfg_librarytodefaultfolder->setToolTip(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/library"));
214 connect(m_configEnv.kcfg_librarytodefaultfolder, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEnableLibraryFolder);
215
216 m_configEnv.kcfg_proxythreads->setMaximum(qMax(1, QThread::idealThreadCount() - 1));
217
218 // Script rendering folder
219 m_configEnv.videofolderurl->setMode(KFile::Directory);
220 m_configEnv.videofolderurl->lineEdit()->setObjectName(QStringLiteral("kcfg_videofolder"));
221 m_configEnv.videofolderurl->setEnabled(!KdenliveSettings::videotodefaultfolder());
222 m_configEnv.videofolderurl->setPlaceholderText(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation));
223 m_configEnv.kcfg_videotodefaultfolder->setToolTip(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation));
224 connect(m_configEnv.kcfg_videotodefaultfolder, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEnableVideoFolder);
225
226 // Mime types
227 QStringList mimes = ClipCreationDialog::getExtensions();
228 std::sort(mimes.begin(), mimes.end());
229 m_configEnv.supportedmimes->setPlainText(mimes.join(QLatin1Char(' ')));
230
231 m_page2 = addPage(p2, i18n("Environment"));
232 m_page2->setIcon(QIcon::fromTheme(QStringLiteral("application-x-executable-script")));
233
234 QWidget *p10 = new QWidget;
235 m_configColors.setupUi(p10);
236 m_page10 = addPage(p10, i18n("Colors"));
237 m_page10->setIcon(QIcon::fromTheme(QStringLiteral("color-management")));
238
239 QWidget *p11 = new QWidget;
240 m_configSpeech.setupUi(p11);
241 m_page11 = addPage(p11, i18n("Speech To Text"));
242 m_page11->setIcon(QIcon::fromTheme(QStringLiteral("text-speak")));
243
244 QWidget *p4 = new QWidget;
245 m_configCapture.setupUi(p4);
246 // Remove ffmpeg tab, unused
247 m_configCapture.tabWidget->removeTab(0);
248 m_configCapture.label->setVisible(false);
249 m_configCapture.kcfg_defaultcapture->setVisible(false);
250 //m_configCapture.tabWidget->removeTab(2);
251 #ifdef USE_V4L
252
253 // Video 4 Linux device detection
254 for (int i = 0; i < 10; ++i) {
255 QString path = QStringLiteral("/dev/video") + QString::number(i);
256 if (QFile::exists(path)) {
257 QStringList deviceInfo = V4lCaptureHandler::getDeviceName(path);
258 if (!deviceInfo.isEmpty()) {
259 m_configCapture.kcfg_detectedv4ldevices->addItem(deviceInfo.at(0), path);
260 m_configCapture.kcfg_detectedv4ldevices->setItemData(m_configCapture.kcfg_detectedv4ldevices->count() - 1, deviceInfo.at(1), Qt::UserRole + 1);
261 }
262 }
263 }
264 connect(m_configCapture.kcfg_detectedv4ldevices, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
265 &KdenliveSettingsDialog::slotUpdatev4lDevice);
266 connect(m_configCapture.kcfg_v4l_format, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
267 &KdenliveSettingsDialog::slotUpdatev4lCaptureProfile);
268 connect(m_configCapture.config_v4l, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditVideo4LinuxProfile);
269
270 slotUpdatev4lDevice();
271 #endif
272
273 m_page4 = addPage(p4, i18n("Capture"));
274 m_page4->setIcon(QIcon::fromTheme(QStringLiteral("media-record")));
275 m_configCapture.tabWidget->setCurrentIndex(KdenliveSettings::defaultcapture());
276 #ifdef Q_WS_MAC
277 m_configCapture.tabWidget->setEnabled(false);
278 m_configCapture.kcfg_defaultcapture->setEnabled(false);
279 m_configCapture.label->setText(i18n("Capture is not yet available on Mac OS X."));
280 #endif
281
282 QWidget *p5 = new QWidget;
283 m_configShuttle.setupUi(p5);
284 #ifdef USE_JOGSHUTTLE
285 m_configShuttle.toolBtnReload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
286 connect(m_configShuttle.kcfg_enableshuttle, &QCheckBox::stateChanged, this, &KdenliveSettingsDialog::slotCheckShuttle);
287 connect(m_configShuttle.shuttledevicelist, SIGNAL(activated(int)), this, SLOT(slotUpdateShuttleDevice(int)));
288 connect(m_configShuttle.toolBtnReload, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotReloadShuttleDevices);
289
290 slotCheckShuttle(static_cast<int>(KdenliveSettings::enableshuttle()));
291 m_configShuttle.shuttledisabled->hide();
292
293 // Store the button pointers into an array for easier handling them in the other functions.
294 // TODO: impl enumerator or live with cut and paste :-)))
295 setupJogshuttleBtns(KdenliveSettings::shuttledevice());
296 #if 0
297 m_shuttle_buttons.push_back(m_configShuttle.shuttle1);
298 m_shuttle_buttons.push_back(m_configShuttle.shuttle2);
299 m_shuttle_buttons.push_back(m_configShuttle.shuttle3);
300 m_shuttle_buttons.push_back(m_configShuttle.shuttle4);
301 m_shuttle_buttons.push_back(m_configShuttle.shuttle5);
302 m_shuttle_buttons.push_back(m_configShuttle.shuttle6);
303 m_shuttle_buttons.push_back(m_configShuttle.shuttle7);
304 m_shuttle_buttons.push_back(m_configShuttle.shuttle8);
305 m_shuttle_buttons.push_back(m_configShuttle.shuttle9);
306 m_shuttle_buttons.push_back(m_configShuttle.shuttle10);
307 m_shuttle_buttons.push_back(m_configShuttle.shuttle11);
308 m_shuttle_buttons.push_back(m_configShuttle.shuttle12);
309 m_shuttle_buttons.push_back(m_configShuttle.shuttle13);
310 m_shuttle_buttons.push_back(m_configShuttle.shuttle14);
311 m_shuttle_buttons.push_back(m_configShuttle.shuttle15);
312 #endif
313
314 #else /* ! USE_JOGSHUTTLE */
315 m_configShuttle.kcfg_enableshuttle->hide();
316 m_configShuttle.kcfg_enableshuttle->setDisabled(true);
317 #endif /* USE_JOGSHUTTLE */
318 m_page5 = addPage(p5, i18n("JogShuttle"));
319 m_page5->setIcon(QIcon::fromTheme(QStringLiteral("dialog-input-devices")));
320
321 QWidget *p6 = new QWidget;
322 m_configSdl.setupUi(p6);
323 m_configSdl.reload_blackmagic->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
324 connect(m_configSdl.reload_blackmagic, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotReloadBlackMagic);
325
326 // m_configSdl.kcfg_openglmonitors->setHidden(true);
327
328 m_page6 = addPage(p6, i18n("Playback"));
329 m_page6->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
330
331 QWidget *p7 = new QWidget;
332 m_configTranscode.setupUi(p7);
333 m_page7 = addPage(p7, i18n("Transcode"));
334 m_page7->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
335
336 connect(m_configTranscode.button_add, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotAddTranscode);
337 connect(m_configTranscode.button_delete, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotDeleteTranscode);
338 connect(m_configTranscode.profiles_list, &QListWidget::itemChanged, this, &KdenliveSettingsDialog::slotDialogModified);
339 connect(m_configTranscode.profiles_list, &QListWidget::currentRowChanged, this, &KdenliveSettingsDialog::slotSetTranscodeProfile);
340 connect(m_configTranscode.profile_description, &QLineEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate);
341 connect(m_configTranscode.profile_extension, &QLineEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate);
342 connect(m_configTranscode.profile_parameters, &QPlainTextEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate);
343 connect(m_configTranscode.profile_audioonly, &QCheckBox::stateChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate);
344
345 connect(m_configTranscode.button_update, &QAbstractButton::pressed, this, &KdenliveSettingsDialog::slotUpdateTranscodingProfile);
346
347 m_configTranscode.profile_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5);
348
349 connect(m_configEnv.kp_image, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditImageApplication);
350 connect(m_configEnv.kp_audio, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditAudioApplication);
351
352 loadEncodingProfiles();
353
354 connect(m_configSdl.fullscreen_monitor, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
355 &KdenliveSettingsDialog::slotSetFullscreenMonitor);
356 connect(m_configSdl.kcfg_audio_driver, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
357 &KdenliveSettingsDialog::slotCheckAlsaDriver);
358 connect(m_configSdl.kcfg_audio_backend, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
359 &KdenliveSettingsDialog::slotCheckAudioBackend);
360 initDevices();
361 connect(m_configCapture.kcfg_grab_capture_type, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
362 &KdenliveSettingsDialog::slotUpdateGrabRegionStatus);
363
364 slotUpdateGrabRegionStatus();
365 loadTranscodeProfiles();
366
367 // decklink profile
368 QAction *act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
369 act->setData(4);
370 connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
371 m_configCapture.decklink_manageprofile->setDefaultAction(act);
372 m_configCapture.decklink_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
373 m_configCapture.decklink_parameters->setVisible(false);
374 m_configCapture.decklink_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 4);
375 m_configCapture.decklink_parameters->setPlainText(KdenliveSettings::decklink_parameters());
376 connect(m_configCapture.kcfg_decklink_profile, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
377 &KdenliveSettingsDialog::slotUpdateDecklinkProfile);
378 connect(m_configCapture.decklink_showprofileinfo, &QAbstractButton::clicked, m_configCapture.decklink_parameters, &QWidget::setVisible);
379
380 // ffmpeg profile
381 m_configCapture.v4l_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
382 m_configCapture.v4l_parameters->setVisible(false);
383 m_configCapture.v4l_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 4);
384 m_configCapture.v4l_parameters->setPlainText(KdenliveSettings::v4l_parameters());
385
386 act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
387 act->setData(2);
388 connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
389 m_configCapture.v4l_manageprofile->setDefaultAction(act);
390 connect(m_configCapture.kcfg_v4l_profile, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
391 &KdenliveSettingsDialog::slotUpdateV4lProfile);
392 connect(m_configCapture.v4l_showprofileinfo, &QAbstractButton::clicked, m_configCapture.v4l_parameters, &QWidget::setVisible);
393
394 // screen grab profile
395 m_configCapture.grab_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
396 m_configCapture.grab_parameters->setVisible(false);
397 m_configCapture.grab_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 4);
398 m_configCapture.grab_parameters->setPlainText(KdenliveSettings::grab_parameters());
399 act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
400 act->setData(3);
401 connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
402 m_configCapture.grab_manageprofile->setDefaultAction(act);
403 connect(m_configCapture.kcfg_grab_profile, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
404 &KdenliveSettingsDialog::slotUpdateGrabProfile);
405 connect(m_configCapture.grab_showprofileinfo, &QAbstractButton::clicked, m_configCapture.grab_parameters, &QWidget::setVisible);
406
407 // audio capture channels
408 m_configCapture.audiocapturechannels->clear();
409 m_configCapture.audiocapturechannels->addItem(i18n("Mono (1 channel)"), 1);
410 m_configCapture.audiocapturechannels->addItem(i18n("Stereo (2 channels)"), 2);
411
412 int channelsIndex = m_configCapture.audiocapturechannels->findData(KdenliveSettings::audiocapturechannels());
413 m_configCapture.audiocapturechannels->setCurrentIndex(qMax(channelsIndex, 0));
414 connect(m_configCapture.audiocapturechannels, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
415 &KdenliveSettingsDialog::slotUpdateAudioCaptureChannels);
416
417 // audio capture sample rate
418 m_configCapture.audiocapturesamplerate->clear();
419 m_configCapture.audiocapturesamplerate->addItem(i18n("44100 Hz"), 44100);
420 m_configCapture.audiocapturesamplerate->addItem(i18n("48000 Hz"), 48000);
421
422 int sampleRateIndex = m_configCapture.audiocapturesamplerate->findData(KdenliveSettings::audiocapturesamplerate());
423 m_configCapture.audiocapturesamplerate->setCurrentIndex(qMax(sampleRateIndex, 0));
424 connect(m_configCapture.audiocapturesamplerate, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
425 &KdenliveSettingsDialog::slotUpdateAudioCaptureSampleRate);
426
427 m_configCapture.labelNoAudioDevices->setVisible(false);
428
429 // Timeline preview
430 act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
431 act->setData(1);
432 connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
433 m_configProject.preview_manageprofile->setDefaultAction(act);
434 connect(m_configProject.kcfg_preview_profile, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
435 &KdenliveSettingsDialog::slotUpdatePreviewProfile);
436 connect(m_configProject.preview_showprofileinfo, &QAbstractButton::clicked, m_configProject.previewparams, &QWidget::setVisible);
437 m_configProject.previewparams->setVisible(false);
438 m_configProject.previewparams->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 3);
439 m_configProject.previewparams->setPlainText(KdenliveSettings::previewparams());
440 m_configProject.preview_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
441 m_configProject.preview_showprofileinfo->setToolTip(i18n("Show default timeline preview parameters"));
442 m_configProject.preview_manageprofile->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
443 m_configProject.preview_manageprofile->setToolTip(i18n("Manage timeline preview profiles"));
444 m_configProject.kcfg_preview_profile->setToolTip(i18n("Select default timeline preview profile"));
445
446 // proxy profile stuff
447 m_configProxy.proxy_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
448 m_configProxy.proxy_showprofileinfo->setToolTip(i18n("Show default profile parameters"));
449 m_configProxy.proxy_manageprofile->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
450 m_configProxy.proxy_manageprofile->setToolTip(i18n("Manage proxy profiles"));
451 m_configProxy.kcfg_proxy_profile->setToolTip(i18n("Select default proxy profile"));
452 m_configProxy.proxyparams->setVisible(false);
453 m_configProxy.proxyparams->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 3);
454 m_configProxy.proxyparams->setPlainText(KdenliveSettings::proxyparams());
455
456 act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
457 act->setData(0);
458 connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
459 m_configProxy.proxy_manageprofile->setDefaultAction(act);
460
461 connect(m_configProxy.proxy_showprofileinfo, &QAbstractButton::clicked, m_configProxy.proxyparams, &QWidget::setVisible);
462 connect(m_configProxy.kcfg_proxy_profile, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
463 &KdenliveSettingsDialog::slotUpdateProxyProfile);
464
465 slotUpdateProxyProfile(-1);
466 slotUpdateV4lProfile(-1);
467 slotUpdateGrabProfile(-1);
468 slotUpdateDecklinkProfile(-1);
469
470 // enable GPU accel only if Movit is found
471 m_configSdl.kcfg_gpu_accel->setEnabled(gpuAllowed);
472 m_configSdl.kcfg_gpu_accel->setToolTip(i18n("GPU processing needs MLT compiled with Movit and Rtaudio modules"));
473
474 getBlackMagicDeviceList(m_configCapture.kcfg_decklink_capturedevice);
475 if (!getBlackMagicOutputDeviceList(m_configSdl.kcfg_blackmagic_output_device)) {
476 // No blackmagic card found
477 m_configSdl.kcfg_external_display->setEnabled(false);
478 }
479
480 initAudioRecDevice();
481 initSpeechPage();
482
483 // Config dialog size
484 KSharedConfigPtr config = KSharedConfig::openConfig();
485 KConfigGroup settingsGroup(config, "settings");
486 QSize optimalSize;
487
488 if (!settingsGroup.exists() || !settingsGroup.hasKey("dialogSize")) {
489 const QSize screenSize = (QGuiApplication::primaryScreen()->availableSize() * 0.9);
490 const QSize targetSize = QSize(1024, 700);
491 optimalSize = targetSize.boundedTo(screenSize);
492 } else {
493 optimalSize = settingsGroup.readEntry("dialogSize", QVariant(size())).toSize();
494 }
495 resize(optimalSize);
496 }
497
498 // static
getBlackMagicDeviceList(QComboBox * devicelist,bool force)499 bool KdenliveSettingsDialog::getBlackMagicDeviceList(QComboBox *devicelist, bool force)
500 {
501 if (!force && !KdenliveSettings::decklink_device_found()) {
502 return false;
503 }
504 Mlt::Profile profile;
505 Mlt::Producer bm(profile, "decklink");
506 int found_devices = 0;
507 if (bm.is_valid()) {
508 bm.set("list_devices", 1);
509 found_devices = bm.get_int("devices");
510 } else {
511 KdenliveSettings::setDecklink_device_found(false);
512 }
513 if (found_devices <= 0) {
514 devicelist->setEnabled(false);
515 return false;
516 }
517 KdenliveSettings::setDecklink_device_found(true);
518 for (int i = 0; i < found_devices; ++i) {
519 char *tmp = qstrdup(QStringLiteral("device.%1").arg(i).toUtf8().constData());
520 devicelist->addItem(bm.get(tmp));
521 delete[] tmp;
522 }
523 return true;
524 }
525
526 // static
initAudioRecDevice()527 bool KdenliveSettingsDialog::initAudioRecDevice()
528 {
529 QStringList audioDevices = pCore->getAudioCaptureDevices();
530
531 //show a hint to the user to know what to check for in case the device list are empty (common issue)
532 m_configCapture.labelNoAudioDevices->setVisible(audioDevices.empty());
533
534 m_configCapture.kcfg_defaultaudiocapture->addItems(audioDevices);
535 connect(m_configCapture.kcfg_defaultaudiocapture, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [&]() {
536 QString currentDevice = m_configCapture.kcfg_defaultaudiocapture->currentText();
537 KdenliveSettings::setDefaultaudiocapture(currentDevice);
538 });
539 QString selectedDevice = KdenliveSettings::defaultaudiocapture();
540 int selectedIndex = m_configCapture.kcfg_defaultaudiocapture->findText(selectedDevice);
541 if (!selectedDevice.isEmpty() && selectedIndex > -1) {
542 m_configCapture.kcfg_defaultaudiocapture->setCurrentIndex(selectedIndex);
543 }
544 return true;
545 }
546
getBlackMagicOutputDeviceList(QComboBox * devicelist,bool force)547 bool KdenliveSettingsDialog::getBlackMagicOutputDeviceList(QComboBox *devicelist, bool force)
548 {
549 if (!force && !KdenliveSettings::decklink_device_found()) {
550 return false;
551 }
552 Mlt::Profile profile;
553 Mlt::Consumer bm(profile, "decklink");
554 int found_devices = 0;
555 if (bm.is_valid()) {
556 bm.set("list_devices", 1);
557 found_devices = bm.get_int("devices");
558 } else {
559 KdenliveSettings::setDecklink_device_found(false);
560 }
561 if (found_devices <= 0) {
562 devicelist->setEnabled(false);
563 return false;
564 }
565 KdenliveSettings::setDecklink_device_found(true);
566 for (int i = 0; i < found_devices; ++i) {
567 char *tmp = qstrdup(QStringLiteral("device.%1").arg(i).toUtf8().constData());
568 devicelist->addItem(bm.get(tmp));
569 delete[] tmp;
570 }
571 devicelist->addItem(QStringLiteral("test"));
572 return true;
573 }
574
setupJogshuttleBtns(const QString & device)575 void KdenliveSettingsDialog::setupJogshuttleBtns(const QString &device)
576 {
577 QList<QComboBox *> list;
578 QList<QLabel *> list1;
579
580 list << m_configShuttle.shuttle1;
581 list << m_configShuttle.shuttle2;
582 list << m_configShuttle.shuttle3;
583 list << m_configShuttle.shuttle4;
584 list << m_configShuttle.shuttle5;
585 list << m_configShuttle.shuttle6;
586 list << m_configShuttle.shuttle7;
587 list << m_configShuttle.shuttle8;
588 list << m_configShuttle.shuttle9;
589 list << m_configShuttle.shuttle10;
590 list << m_configShuttle.shuttle11;
591 list << m_configShuttle.shuttle12;
592 list << m_configShuttle.shuttle13;
593 list << m_configShuttle.shuttle14;
594 list << m_configShuttle.shuttle15;
595
596 list1 << m_configShuttle.label_2; // #1
597 list1 << m_configShuttle.label_4; // #2
598 list1 << m_configShuttle.label_3; // #3
599 list1 << m_configShuttle.label_7; // #4
600 list1 << m_configShuttle.label_5; // #5
601 list1 << m_configShuttle.label_6; // #6
602 list1 << m_configShuttle.label_8; // #7
603 list1 << m_configShuttle.label_9; // #8
604 list1 << m_configShuttle.label_10; // #9
605 list1 << m_configShuttle.label_11; // #10
606 list1 << m_configShuttle.label_12; // #11
607 list1 << m_configShuttle.label_13; // #12
608 list1 << m_configShuttle.label_14; // #13
609 list1 << m_configShuttle.label_15; // #14
610 list1 << m_configShuttle.label_16; // #15
611
612 for (int i = 0; i < list.count(); ++i) {
613 list[i]->hide();
614 list1[i]->hide();
615 }
616 #ifdef USE_JOGSHUTTLE
617 if (!m_configShuttle.kcfg_enableshuttle->isChecked()) {
618 return;
619 }
620 int keysCount = JogShuttle::keysCount(device);
621
622 for (int i = 0; i < keysCount; ++i) {
623 m_shuttle_buttons.push_back(list[i]);
624 list[i]->show();
625 list1[i]->show();
626 }
627
628 // populate the buttons with the current configuration. The items are sorted
629 // according to the user-selected language, so they do not appear in random order.
630 QMap<QString, QString> mappable_actions(m_mappable_actions);
631 QList<QString> action_names = mappable_actions.keys();
632 QList<QString>::Iterator iter = action_names.begin();
633 // qCDebug(KDENLIVE_LOG) << "::::::::::::::::";
634 while (iter != action_names.end()) {
635 // qCDebug(KDENLIVE_LOG) << *iter;
636 ++iter;
637 }
638
639 // qCDebug(KDENLIVE_LOG) << "::::::::::::::::";
640
641 std::sort(action_names.begin(), action_names.end());
642 iter = action_names.begin();
643 while (iter != action_names.end()) {
644 // qCDebug(KDENLIVE_LOG) << *iter;
645 ++iter;
646 }
647 // qCDebug(KDENLIVE_LOG) << "::::::::::::::::";
648
649 // Here we need to compute the action_id -> index-in-action_names. We iterate over the
650 // action_names, as the sorting may depend on the user-language.
651 QStringList actions_map = JogShuttleConfig::actionMap(KdenliveSettings::shuttlebuttons());
652 QMap<QString, int> action_pos;
653 for (const QString &action_id : qAsConst(actions_map)) {
654 // This loop find out at what index is the string that would map to the action_id.
655 for (int i = 0; i < action_names.size(); ++i) {
656 if (mappable_actions[action_names.at(i)] == action_id) {
657 action_pos[action_id] = i;
658 break;
659 }
660 }
661 }
662
663 int i = 0;
664 for (QComboBox *button : qAsConst(m_shuttle_buttons)) {
665 button->addItems(action_names);
666 connect(button, SIGNAL(activated(int)), this, SLOT(slotShuttleModified()));
667 ++i;
668 if (i < actions_map.size()) {
669 button->setCurrentIndex(action_pos[actions_map[i]]);
670 }
671 }
672 #else
673 Q_UNUSED(device)
674 #endif
675 }
676
677 KdenliveSettingsDialog::~KdenliveSettingsDialog() = default;
678
slotUpdateGrabRegionStatus()679 void KdenliveSettingsDialog::slotUpdateGrabRegionStatus()
680 {
681 m_configCapture.region_group->setHidden(m_configCapture.kcfg_grab_capture_type->currentIndex() != 1);
682 }
683
slotEnableCaptureFolder()684 void KdenliveSettingsDialog::slotEnableCaptureFolder()
685 {
686 m_configEnv.capturefolderurl->setEnabled(!m_configEnv.kcfg_capturetoprojectfolder->isChecked());
687 }
688
slotEnableLibraryFolder()689 void KdenliveSettingsDialog::slotEnableLibraryFolder()
690 {
691 m_configEnv.libraryfolderurl->setEnabled(!m_configEnv.kcfg_librarytodefaultfolder->isChecked());
692 }
693
slotEnableVideoFolder()694 void KdenliveSettingsDialog::slotEnableVideoFolder()
695 {
696 m_configEnv.videofolderurl->setEnabled(!m_configEnv.kcfg_videotodefaultfolder->isChecked());
697 }
698
initDevices()699 void KdenliveSettingsDialog::initDevices()
700 {
701 // Fill audio drivers
702 m_configSdl.kcfg_audio_driver->addItem(i18n("Automatic"), QString());
703 #if defined(Q_OS_WIN)
704 //TODO: i18n
705 m_configSdl.kcfg_audio_driver->addItem("DirectSound", "directsound");
706 m_configSdl.kcfg_audio_driver->addItem("WinMM", "winmm");
707 m_configSdl.kcfg_audio_driver->addItem("Wasapi", "wasapi");
708 #elif !defined(Q_WS_MAC)
709 m_configSdl.kcfg_audio_driver->addItem(i18n("ALSA"), "alsa");
710 m_configSdl.kcfg_audio_driver->addItem(i18n("PulseAudio"), "pulseaudio");
711 m_configSdl.kcfg_audio_driver->addItem(i18n("OSS"), "dsp");
712 //m_configSdl.kcfg_audio_driver->addItem(i18n("OSS with DMA access"), "dma");
713 m_configSdl.kcfg_audio_driver->addItem(i18n("Esound daemon"), "esd");
714 m_configSdl.kcfg_audio_driver->addItem(i18n("ARTS daemon"), "artsc");
715 #endif
716
717 if (!KdenliveSettings::audiodrivername().isEmpty())
718 for (int i = 1; i < m_configSdl.kcfg_audio_driver->count(); ++i) {
719 if (m_configSdl.kcfg_audio_driver->itemData(i).toString() == KdenliveSettings::audiodrivername()) {
720 m_configSdl.kcfg_audio_driver->setCurrentIndex(i);
721 KdenliveSettings::setAudio_driver(uint(i));
722 }
723 }
724
725 // Fill the list of audio playback / recording devices
726 m_configSdl.kcfg_audio_device->addItem(i18n("Default"), QString());
727 m_configCapture.kcfg_v4l_alsadevice->addItem(i18n("Default"), "default");
728 if (!QStandardPaths::findExecutable(QStringLiteral("aplay")).isEmpty()) {
729 m_readProcess.setOutputChannelMode(KProcess::OnlyStdoutChannel);
730 m_readProcess.setProgram(QStringLiteral("aplay"), QStringList() << QStringLiteral("-l"));
731 connect(&m_readProcess, &KProcess::readyReadStandardOutput, this, &KdenliveSettingsDialog::slotReadAudioDevices);
732 m_readProcess.execute(5000);
733 } else {
734 // If aplay is not installed on the system, parse the /proc/asound/pcm file
735 QFile file(QStringLiteral("/proc/asound/pcm"));
736 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
737 QTextStream stream(&file);
738 QString line = stream.readLine();
739 QString deviceId;
740 while (!line.isNull()) {
741 if (line.contains(QStringLiteral("playback"))) {
742 deviceId = line.section(QLatin1Char(':'), 0, 0);
743 m_configSdl.kcfg_audio_device->addItem(line.section(QLatin1Char(':'), 1, 1), QStringLiteral("plughw:%1,%2")
744 .arg(deviceId.section(QLatin1Char('-'), 0, 0).toInt())
745 .arg(deviceId.section(QLatin1Char('-'), 1, 1).toInt()));
746 }
747 if (line.contains(QStringLiteral("capture"))) {
748 deviceId = line.section(QLatin1Char(':'), 0, 0);
749 m_configCapture.kcfg_v4l_alsadevice->addItem(
750 line.section(QLatin1Char(':'), 1, 1).simplified(),
751 QStringLiteral("hw:%1,%2").arg(deviceId.section(QLatin1Char('-'), 0, 0).toInt()).arg(deviceId.section(QLatin1Char('-'), 1, 1).toInt()));
752 }
753 line = stream.readLine();
754 }
755 file.close();
756 } else {
757 qCDebug(KDENLIVE_LOG) << " / / / /CANNOT READ PCM";
758 }
759 }
760
761 // Add pulseaudio capture option
762 m_configCapture.kcfg_v4l_alsadevice->addItem(i18n("PulseAudio"), "pulse");
763
764 if (!KdenliveSettings::audiodevicename().isEmpty()) {
765 // Select correct alsa device
766 int ix = m_configSdl.kcfg_audio_device->findData(KdenliveSettings::audiodevicename());
767 m_configSdl.kcfg_audio_device->setCurrentIndex(ix);
768 KdenliveSettings::setAudio_device(ix);
769 }
770
771 if (!KdenliveSettings::v4l_alsadevicename().isEmpty()) {
772 // Select correct alsa device
773 int ix = m_configCapture.kcfg_v4l_alsadevice->findData(KdenliveSettings::v4l_alsadevicename());
774 m_configCapture.kcfg_v4l_alsadevice->setCurrentIndex(ix);
775 KdenliveSettings::setV4l_alsadevice(ix);
776 }
777
778 m_configSdl.kcfg_audio_backend->addItem(i18n("SDL"), KdenliveSettings::sdlAudioBackend());
779 m_configSdl.kcfg_audio_backend->addItem(i18n("RtAudio"), "rtaudio");
780
781 if (!KdenliveSettings::audiobackend().isEmpty()) {
782 int ix = m_configSdl.kcfg_audio_backend->findData(KdenliveSettings::audiobackend());
783 m_configSdl.kcfg_audio_backend->setCurrentIndex(ix);
784 KdenliveSettings::setAudio_backend(ix);
785 }
786 m_configSdl.group_sdl->setEnabled(KdenliveSettings::audiobackend().startsWith(QLatin1String("sdl")));
787
788 // Fill monitors data
789 QSignalBlocker bk(m_configSdl.fullscreen_monitor);
790 m_configSdl.fullscreen_monitor->clear();
791 m_configSdl.fullscreen_monitor->addItem(i18n("auto"));
792 for (const QScreen* screen : qApp->screens()) {
793 m_configSdl.fullscreen_monitor->addItem(QString("%1 %2 (%3)").arg(screen->manufacturer(), screen->model(), screen->name()), screen->serialNumber());
794 }
795 if (!KdenliveSettings::fullscreen_monitor().isEmpty()) {
796 int ix = m_configSdl.fullscreen_monitor->findData(KdenliveSettings::fullscreen_monitor());
797 if (ix > -1) {
798 m_configSdl.fullscreen_monitor->setCurrentIndex(ix);
799 }
800 }
801
802 loadCurrentV4lProfileInfo();
803 }
804
slotReadAudioDevices()805 void KdenliveSettingsDialog::slotReadAudioDevices()
806 {
807 QString result = QString(m_readProcess.readAllStandardOutput());
808 // qCDebug(KDENLIVE_LOG) << "// / / / / / READING APLAY: ";
809 // qCDebug(KDENLIVE_LOG) << result;
810 const QStringList lines = result.split(QLatin1Char('\n'));
811 for (const QString &devicestr : lines) {
812 ////qCDebug(KDENLIVE_LOG) << "// READING LINE: " << data;
813 if (!devicestr.startsWith(QLatin1Char(' ')) && devicestr.count(QLatin1Char(':')) > 1) {
814 QString card = devicestr.section(QLatin1Char(':'), 0, 0).section(QLatin1Char(' '), -1);
815 QString device = devicestr.section(QLatin1Char(':'), 1, 1).section(QLatin1Char(' '), -1);
816 m_configSdl.kcfg_audio_device->addItem(devicestr.section(QLatin1Char(':'), -1).simplified(), QStringLiteral("plughw:%1,%2").arg(card, device));
817 m_configCapture.kcfg_v4l_alsadevice->addItem(devicestr.section(QLatin1Char(':'), -1).simplified(),
818 QStringLiteral("hw:%1,%2").arg(card, device));
819 }
820 }
821 }
822
showPage(int page,int option)823 void KdenliveSettingsDialog::showPage(int page, int option)
824 {
825 switch (page) {
826 case 1:
827 setCurrentPage(m_page1);
828 break;
829 case 2:
830 setCurrentPage(m_page2);
831 break;
832 case 3:
833 setCurrentPage(m_page3);
834 break;
835 case 4:
836 setCurrentPage(m_page4);
837 m_configCapture.tabWidget->setCurrentIndex(option);
838 break;
839 case 5:
840 setCurrentPage(m_page5);
841 break;
842 case 6:
843 setCurrentPage(m_page6);
844 break;
845 case 7:
846 setCurrentPage(m_page7);
847 break;
848 case 8:
849 setCurrentPage(m_page11);
850 checkVoskDependencies();
851 break;
852 default:
853 setCurrentPage(m_page1);
854 }
855 }
856
slotEditAudioApplication()857 void KdenliveSettingsDialog::slotEditAudioApplication()
858 {
859 KService::Ptr service;
860 QPointer<KOpenWithDialog> dlg = new KOpenWithDialog(QList<QUrl>(), i18n("Select default audio editor"), m_configEnv.kcfg_defaultaudioapp->text(), this);
861 if (dlg->exec() == QDialog::Accepted) {
862 service = dlg->service();
863 m_configEnv.kcfg_defaultaudioapp->setText(KIO::DesktopExecParser::executablePath(service->exec()));
864 }
865
866 delete dlg;
867 }
868
slotEditImageApplication()869 void KdenliveSettingsDialog::slotEditImageApplication()
870 {
871 QPointer<KOpenWithDialog> dlg = new KOpenWithDialog(QList<QUrl>(), i18n("Select default image editor"), m_configEnv.kcfg_defaultimageapp->text(), this);
872 if (dlg->exec() == QDialog::Accepted) {
873 KService::Ptr service = dlg->service();
874 m_configEnv.kcfg_defaultimageapp->setText(KIO::DesktopExecParser::executablePath(service->exec()));
875 }
876 delete dlg;
877 }
878
slotCheckShuttle(int state)879 void KdenliveSettingsDialog::slotCheckShuttle(int state)
880 {
881 #ifdef USE_JOGSHUTTLE
882 m_configShuttle.config_group->setEnabled(state != Qt::Unchecked);
883 m_configShuttle.shuttledevicelist->clear();
884
885 QStringList devNames = KdenliveSettings::shuttledevicenames();
886 QStringList devPaths = KdenliveSettings::shuttledevicepaths();
887
888 if (devNames.count() != devPaths.count()) {
889 return;
890 }
891 for (int i = 0; i < devNames.count(); ++i) {
892 m_configShuttle.shuttledevicelist->addItem(devNames.at(i), devPaths.at(i));
893 }
894 if (state != Qt::Unchecked) {
895 setupJogshuttleBtns(m_configShuttle.shuttledevicelist->itemData(m_configShuttle.shuttledevicelist->currentIndex()).toString());
896 }
897 #else
898 Q_UNUSED(state)
899 #endif /* USE_JOGSHUTTLE */
900 }
901
slotUpdateShuttleDevice(int ix)902 void KdenliveSettingsDialog::slotUpdateShuttleDevice(int ix)
903 {
904 #ifdef USE_JOGSHUTTLE
905 QString device = m_configShuttle.shuttledevicelist->itemData(ix).toString();
906 // KdenliveSettings::setShuttledevice(device);
907 setupJogshuttleBtns(device);
908 m_configShuttle.kcfg_shuttledevice->setText(device);
909 #else
910 Q_UNUSED(ix)
911 #endif /* USE_JOGSHUTTLE */
912 }
913
updateWidgets()914 void KdenliveSettingsDialog::updateWidgets()
915 {
916 // Revert widgets to last saved state (for example when user pressed "Cancel")
917 // //qCDebug(KDENLIVE_LOG) << "// // // KCONFIG Revert called";
918 #ifdef USE_JOGSHUTTLE
919 // revert jog shuttle device
920 if (m_configShuttle.shuttledevicelist->count() > 0) {
921 for (int i = 0; i < m_configShuttle.shuttledevicelist->count(); ++i) {
922 if (m_configShuttle.shuttledevicelist->itemData(i) == KdenliveSettings::shuttledevice()) {
923 m_configShuttle.shuttledevicelist->setCurrentIndex(i);
924 break;
925 }
926 }
927 }
928
929 // Revert jog shuttle buttons
930 QList<QString> action_names = m_mappable_actions.keys();
931 std::sort(action_names.begin(), action_names.end());
932 QStringList actions_map = JogShuttleConfig::actionMap(KdenliveSettings::shuttlebuttons());
933 QMap<QString, int> action_pos;
934 for (const QString &action_id : qAsConst(actions_map)) {
935 // This loop find out at what index is the string that would map to the action_id.
936 for (int i = 0; i < action_names.size(); ++i) {
937 if (m_mappable_actions[action_names[i]] == action_id) {
938 action_pos[action_id] = i;
939 break;
940 }
941 }
942 }
943 int i = 0;
944 for (QComboBox *button : qAsConst(m_shuttle_buttons)) {
945 ++i;
946 if (i < actions_map.size()) {
947 button->setCurrentIndex(action_pos[actions_map[i]]);
948 }
949 }
950 #endif /* USE_JOGSHUTTLE */
951 }
952
accept()953 void KdenliveSettingsDialog::accept()
954 {
955 if (m_pw->selectedProfile().isEmpty()) {
956 KMessageBox::error(this, i18n("Please select a video profile"));
957 return;
958 }
959 KConfigDialog::accept();
960 }
961
updateSettings()962 void KdenliveSettingsDialog::updateSettings()
963 {
964 // Save changes to settings (for example when user pressed "Apply" or "Ok")
965 // //qCDebug(KDENLIVE_LOG) << "// // // KCONFIG UPDATE called";
966 if (m_pw->selectedProfile().isEmpty()) {
967 KMessageBox::error(this, i18n("Please select a video profile"));
968 return;
969 }
970 KdenliveSettings::setDefault_profile(m_pw->selectedProfile());
971
972 if (m_configEnv.ffmpegurl->text().isEmpty()) {
973 QString infos;
974 QString warnings;
975 Wizard::slotCheckPrograms(infos, warnings);
976 m_configEnv.ffmpegurl->setText(KdenliveSettings::ffmpegpath());
977 m_configEnv.ffplayurl->setText(KdenliveSettings::ffplaypath());
978 m_configEnv.ffprobeurl->setText(KdenliveSettings::ffprobepath());
979 }
980 if (m_configEnv.mediainfourl->text().isEmpty()) {
981 m_configEnv.mediainfourl->setText(KdenliveSettings::mediainfopath());
982 }
983
984 if (m_configTimeline.kcfg_trackheight->value() == 0) {
985 QFont ft = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
986 // Default unit for timeline.qml objects size
987 int baseUnit = qMax(28, int(QFontInfo(ft).pixelSize() * 1.8 + 0.5));
988 int trackHeight = qMax(50, int(2.2 * baseUnit + 6));
989 m_configTimeline.kcfg_trackheight->setValue(trackHeight);
990 } else if (m_configTimeline.kcfg_trackheight->value() != KdenliveSettings::trackheight()) {
991 QFont ft = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont);
992 // Default unit for timeline.qml objects size
993 int baseUnit = qMax(28, int(QFontInfo(ft).pixelSize() * 1.8 + 0.5));
994 if (m_configTimeline.kcfg_trackheight->value() < baseUnit) {
995 m_configTimeline.kcfg_trackheight->setValue(baseUnit);
996 }
997 }
998
999 bool resetConsumer = false;
1000 bool fullReset = false;
1001 bool updateCapturePath = false;
1002 bool updateLibrary = false;
1003
1004 /*if (m_configShuttle.shuttledevicelist->count() > 0) {
1005 QString device = m_configShuttle.shuttledevicelist->itemData(m_configShuttle.shuttledevicelist->currentIndex()).toString();
1006 if (device != KdenliveSettings::shuttledevice()) KdenliveSettings::setShuttledevice(device);
1007 }*/
1008
1009 // Capture default folder
1010 if (m_configEnv.kcfg_capturetoprojectfolder->isChecked() != KdenliveSettings::capturetoprojectfolder()) {
1011 KdenliveSettings::setCapturetoprojectfolder(m_configEnv.kcfg_capturetoprojectfolder->isChecked());
1012 updateCapturePath = true;
1013 }
1014
1015 if (m_configProject.projecturl->url().toLocalFile() != KdenliveSettings::defaultprojectfolder()) {
1016 KdenliveSettings::setDefaultprojectfolder(m_configProject.projecturl->url().toLocalFile());
1017 }
1018
1019 if (m_configEnv.capturefolderurl->url().toLocalFile() != KdenliveSettings::capturefolder()) {
1020 KdenliveSettings::setCapturefolder(m_configEnv.capturefolderurl->url().toLocalFile());
1021 updateCapturePath = true;
1022 }
1023
1024 // Library default folder
1025 if (m_configEnv.kcfg_librarytodefaultfolder->isChecked() != KdenliveSettings::librarytodefaultfolder()) {
1026 KdenliveSettings::setLibrarytodefaultfolder(m_configEnv.kcfg_librarytodefaultfolder->isChecked());
1027 updateLibrary = true;
1028 }
1029
1030 if (m_configEnv.libraryfolderurl->url().toLocalFile() != KdenliveSettings::libraryfolder()) {
1031 KdenliveSettings::setLibraryfolder(m_configEnv.libraryfolderurl->url().toLocalFile());
1032 if (!KdenliveSettings::librarytodefaultfolder()) {
1033 updateLibrary = true;
1034 }
1035 }
1036
1037 if (m_configCapture.kcfg_v4l_format->currentIndex() != int(KdenliveSettings::v4l_format())) {
1038 saveCurrentV4lProfile();
1039 KdenliveSettings::setV4l_format(0);
1040 }
1041
1042 // Check if screengrab is fullscreen
1043 if (m_configCapture.kcfg_grab_capture_type->currentIndex() != KdenliveSettings::grab_capture_type()) {
1044 KdenliveSettings::setGrab_capture_type(m_configCapture.kcfg_grab_capture_type->currentIndex());
1045 emit updateFullScreenGrab();
1046 }
1047
1048 // Check encoding profiles
1049 // FFmpeg
1050 QString profilestr = m_configCapture.kcfg_v4l_profile->currentData().toString();
1051 if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::v4l_parameters() ||
1052 profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::v4l_extension())) {
1053 KdenliveSettings::setV4l_parameters(profilestr.section(QLatin1Char(';'), 0, 0));
1054 KdenliveSettings::setV4l_extension(profilestr.section(QLatin1Char(';'), 1, 1));
1055 }
1056 // screengrab
1057 profilestr = m_configCapture.kcfg_grab_profile->currentData().toString();
1058 if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::grab_parameters() ||
1059 profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::grab_extension())) {
1060 KdenliveSettings::setGrab_parameters(profilestr.section(QLatin1Char(';'), 0, 0));
1061 KdenliveSettings::setGrab_extension(profilestr.section(QLatin1Char(';'), 1, 1));
1062 }
1063
1064 // decklink
1065 profilestr = m_configCapture.kcfg_decklink_profile->currentData().toString();
1066 if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::decklink_parameters() ||
1067 profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::decklink_extension())) {
1068 KdenliveSettings::setDecklink_parameters(profilestr.section(QLatin1Char(';'), 0, 0));
1069 KdenliveSettings::setDecklink_extension(profilestr.section(QLatin1Char(';'), 1, 1));
1070 }
1071 // proxies
1072 profilestr = m_configProxy.kcfg_proxy_profile->currentData().toString();
1073 if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::proxyparams() ||
1074 profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::proxyextension())) {
1075 KdenliveSettings::setProxyparams(profilestr.section(QLatin1Char(';'), 0, 0));
1076 KdenliveSettings::setProxyextension(profilestr.section(QLatin1Char(';'), 1, 1));
1077 }
1078
1079 // external proxies
1080 profilestr = m_configProxy.kcfg_external_proxy_profile->currentData().toString();
1081 if (!profilestr.isEmpty() && (profilestr != KdenliveSettings::externalProxyProfile())) {
1082 KdenliveSettings::setExternalProxyProfile(profilestr);
1083 }
1084
1085 // timeline preview
1086 profilestr = m_configProject.kcfg_preview_profile->currentData().toString();
1087 if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::previewparams() ||
1088 profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::previewextension())) {
1089 KdenliveSettings::setPreviewparams(profilestr.section(QLatin1Char(';'), 0, 0));
1090 KdenliveSettings::setPreviewextension(profilestr.section(QLatin1Char(';'), 1, 1));
1091 }
1092
1093 if (updateCapturePath) {
1094 emit updateCaptureFolder();
1095 }
1096 if (updateLibrary) {
1097 emit updateLibraryFolder();
1098 }
1099
1100 QString value = m_configCapture.kcfg_v4l_alsadevice->currentData().toString();
1101 if (value != KdenliveSettings::v4l_alsadevicename()) {
1102 KdenliveSettings::setV4l_alsadevicename(value);
1103 }
1104
1105 if (m_configSdl.kcfg_external_display->isChecked() != KdenliveSettings::external_display()) {
1106 KdenliveSettings::setExternal_display(m_configSdl.kcfg_external_display->isChecked());
1107 resetConsumer = true;
1108 fullReset = true;
1109 } else if (KdenliveSettings::external_display() &&
1110 KdenliveSettings::blackmagic_output_device() != m_configSdl.kcfg_blackmagic_output_device->currentIndex()) {
1111 resetConsumer = true;
1112 fullReset = true;
1113 }
1114
1115 value = m_configSdl.kcfg_audio_driver->currentData().toString();
1116 if (value != KdenliveSettings::audiodrivername()) {
1117 KdenliveSettings::setAudiodrivername(value);
1118 resetConsumer = true;
1119 }
1120
1121 if (value == QLatin1String("alsa")) {
1122 // Audio device setting is only valid for alsa driver
1123 value = m_configSdl.kcfg_audio_device->currentData().toString();
1124 if (value != KdenliveSettings::audiodevicename()) {
1125 KdenliveSettings::setAudiodevicename(value);
1126 resetConsumer = true;
1127 }
1128 } else if (!KdenliveSettings::audiodevicename().isEmpty()) {
1129 KdenliveSettings::setAudiodevicename(QString());
1130 resetConsumer = true;
1131 }
1132
1133 value = m_configSdl.kcfg_audio_backend->currentData().toString();
1134 if (value != KdenliveSettings::audiobackend()) {
1135 KdenliveSettings::setAudiobackend(value);
1136 resetConsumer = true;
1137 fullReset = true;
1138 }
1139
1140 if (m_configSdl.kcfg_window_background->color() != KdenliveSettings::window_background()) {
1141 KdenliveSettings::setWindow_background(m_configSdl.kcfg_window_background->color());
1142 emit updateMonitorBg();
1143 }
1144
1145 if (m_configColors.kcfg_thumbColor1->color() != KdenliveSettings::thumbColor1() || m_configColors.kcfg_thumbColor2->color() != KdenliveSettings::thumbColor2()) {
1146 KdenliveSettings::setThumbColor1(m_configColors.kcfg_thumbColor1->color());
1147 KdenliveSettings::setThumbColor2(m_configColors.kcfg_thumbColor2->color());
1148 emit pCore->window()->getMainTimeline()->controller()->colorsChanged();
1149 pCore->getMonitor(Kdenlive::ClipMonitor)->refreshAudioThumbs();
1150 }
1151
1152 if (m_configSdl.kcfg_volume->value() != KdenliveSettings::volume()) {
1153 KdenliveSettings::setVolume(m_configSdl.kcfg_volume->value());
1154 resetConsumer = true;
1155 }
1156
1157 if (m_configMisc.kcfg_tabposition->currentIndex() != KdenliveSettings::tabposition()) {
1158 KdenliveSettings::setTabposition(m_configMisc.kcfg_tabposition->currentIndex());
1159 }
1160
1161 if (m_configTimeline.kcfg_displayallchannels->isChecked() != KdenliveSettings::displayallchannels()) {
1162 KdenliveSettings::setDisplayallchannels(m_configTimeline.kcfg_displayallchannels->isChecked());
1163 emit audioThumbFormatChanged();
1164 pCore->getMonitor(Kdenlive::ClipMonitor)->refreshAudioThumbs();
1165 }
1166
1167 if (m_modified) {
1168 // The transcoding profiles were modified, save.
1169 m_modified = false;
1170 saveTranscodeProfiles();
1171 }
1172
1173 #ifdef USE_JOGSHUTTLE
1174 m_shuttleModified = false;
1175
1176 QStringList actions;
1177 actions << QStringLiteral("monitor_pause"); // the Job rest position action.
1178 for (QComboBox *button : qAsConst(m_shuttle_buttons)) {
1179 actions << m_mappable_actions[button->currentText()];
1180 }
1181 QString maps = JogShuttleConfig::actionMap(actions);
1182 // fprintf(stderr, "Shuttle config: %s\n", JogShuttleConfig::actionMap(actions).toLatin1().constData());
1183 if (KdenliveSettings::shuttlebuttons() != maps) {
1184 KdenliveSettings::setShuttlebuttons(maps);
1185 }
1186 #endif
1187
1188 bool restart = false;
1189 if (m_configSdl.kcfg_gpu_accel->isChecked() != KdenliveSettings::gpu_accel()) {
1190 // GPU setting was changed, we need to restart Kdenlive or everything will be corrupted
1191 if (KMessageBox::warningContinueCancel(this, i18n("Kdenlive must be restarted to change this setting")) == KMessageBox::Continue) {
1192 restart = true;
1193 } else {
1194 m_configSdl.kcfg_gpu_accel->setChecked(KdenliveSettings::gpu_accel());
1195 }
1196 }
1197
1198 if (m_configTimeline.kcfg_trackheight->value() != KdenliveSettings::trackheight()) {
1199 KdenliveSettings::setTrackheight(m_configTimeline.kcfg_trackheight->value());
1200 emit resetView();
1201 }
1202
1203 if (m_configTimeline.kcfg_autoscroll->isChecked() != KdenliveSettings::autoscroll()) {
1204 KdenliveSettings::setAutoscroll(m_configTimeline.kcfg_autoscroll->isChecked());
1205 emit pCore->autoScrollChanged();
1206 }
1207
1208 if (m_configTimeline.kcfg_pauseonseek->isChecked() != KdenliveSettings::pauseonseek()) {
1209 KdenliveSettings::setPauseonseek(m_configTimeline.kcfg_pauseonseek->isChecked());
1210 }
1211
1212 if (m_configTimeline.kcfg_scrollvertically->isChecked() != KdenliveSettings::scrollvertically()) {
1213 KdenliveSettings::setScrollvertically(m_configTimeline.kcfg_scrollvertically->isChecked());
1214 emit pCore->window()->getMainTimeline()->controller()->scrollVerticallyChanged();
1215 }
1216
1217 // Mimes
1218 if (m_configEnv.kcfg_addedExtensions->text() != KdenliveSettings::addedExtensions()) {
1219 // Update list
1220 KdenliveSettings::setAddedExtensions(m_configEnv.kcfg_addedExtensions->text());
1221 QStringList mimes = ClipCreationDialog::getExtensions();
1222 std::sort(mimes.begin(), mimes.end());
1223 m_configEnv.supportedmimes->setPlainText(mimes.join(QLatin1Char(' ')));
1224 }
1225
1226 // proxy/transcode max concurrent jobs
1227 if (m_configEnv.kcfg_proxythreads->value() != KdenliveSettings::proxythreads()) {
1228 KdenliveSettings::setProxythreads(m_configEnv.kcfg_proxythreads->value());
1229 pCore->taskManager.updateConcurrency();
1230 }
1231
1232
1233 KConfigDialog::settingsChangedSlot();
1234 // KConfigDialog::updateSettings();
1235 if (resetConsumer) {
1236 emit doResetConsumer(fullReset);
1237 }
1238 if (restart) {
1239 emit restartKdenlive();
1240 }
1241 emit checkTabPosition();
1242
1243 // remembering Config dialog size
1244 KSharedConfigPtr config = KSharedConfig::openConfig();
1245 KConfigGroup settingsGroup(config, "settings");
1246 settingsGroup.writeEntry("dialogSize", QVariant(size()));
1247 }
1248
slotSetFullscreenMonitor()1249 void KdenliveSettingsDialog::slotSetFullscreenMonitor()
1250 {
1251 QString value = m_configSdl.fullscreen_monitor->currentData().toString();
1252 KdenliveSettings::setFullscreen_monitor(value);
1253 }
1254
slotCheckAlsaDriver()1255 void KdenliveSettingsDialog::slotCheckAlsaDriver()
1256 {
1257 QString value = m_configSdl.kcfg_audio_driver->currentData().toString();
1258 m_configSdl.kcfg_audio_device->setEnabled(value == QLatin1String("alsa"));
1259 }
1260
slotCheckAudioBackend()1261 void KdenliveSettingsDialog::slotCheckAudioBackend()
1262 {
1263 QString value = m_configSdl.kcfg_audio_backend->currentData().toString();
1264 m_configSdl.group_sdl->setEnabled(value.startsWith(QLatin1String("sdl")));
1265 }
1266
loadTranscodeProfiles()1267 void KdenliveSettingsDialog::loadTranscodeProfiles()
1268 {
1269 KSharedConfigPtr config =
1270 KSharedConfig::openConfig(QStringLiteral("kdenlivetranscodingrc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
1271 KConfigGroup transConfig(config, "Transcoding");
1272 // read the entries
1273 m_configTranscode.profiles_list->blockSignals(true);
1274 m_configTranscode.profiles_list->clear();
1275 QMap<QString, QString> profiles = transConfig.entryMap();
1276 QMapIterator<QString, QString> i(profiles);
1277 while (i.hasNext()) {
1278 i.next();
1279 auto *item = new QListWidgetItem(i.key());
1280 QString profilestr = i.value();
1281 if (profilestr.contains(QLatin1Char(';'))) {
1282 item->setToolTip(profilestr.section(QLatin1Char(';'), 1, 1));
1283 }
1284 item->setData(Qt::UserRole, profilestr);
1285 m_configTranscode.profiles_list->addItem(item);
1286 // item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
1287 }
1288 m_configTranscode.profiles_list->blockSignals(false);
1289 m_configTranscode.profiles_list->setCurrentRow(0);
1290 }
1291
saveTranscodeProfiles()1292 void KdenliveSettingsDialog::saveTranscodeProfiles()
1293 {
1294 QString transcodeFile = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/kdenlivetranscodingrc");
1295 KSharedConfigPtr config = KSharedConfig::openConfig(transcodeFile);
1296 KConfigGroup transConfig(config, "Transcoding");
1297 // read the entries
1298 transConfig.deleteGroup();
1299 int max = m_configTranscode.profiles_list->count();
1300 for (int i = 0; i < max; ++i) {
1301 QListWidgetItem *item = m_configTranscode.profiles_list->item(i);
1302 transConfig.writeEntry(item->text(), item->data(Qt::UserRole).toString());
1303 }
1304 config->sync();
1305 }
1306
slotAddTranscode()1307 void KdenliveSettingsDialog::slotAddTranscode()
1308 {
1309
1310 bool ok;
1311 QString presetName = QInputDialog::getText(this, i18n("Enter preset name"), i18n("Enter the name of this preset"), QLineEdit::Normal, QString(), &ok);
1312 if (!ok) return;
1313 if (!m_configTranscode.profiles_list->findItems(presetName, Qt::MatchExactly).isEmpty()) {
1314 KMessageBox::sorry(this, i18n("A profile with that name already exists"));
1315 return;
1316 }
1317 QListWidgetItem *item = new QListWidgetItem(presetName);
1318 QString profilestr = m_configTranscode.profile_parameters->toPlainText();
1319 profilestr.append(" %1." + m_configTranscode.profile_extension->text());
1320 profilestr.append(';');
1321 if (!m_configTranscode.profile_description->text().isEmpty()) {
1322 profilestr.append(m_configTranscode.profile_description->text());
1323 }
1324 if (m_configTranscode.profile_audioonly->isChecked()) {
1325 profilestr.append(";audio");
1326 }
1327 item->setData(Qt::UserRole, profilestr);
1328 m_configTranscode.profiles_list->addItem(item);
1329 m_configTranscode.profiles_list->setCurrentItem(item);
1330 slotDialogModified();
1331 }
1332
slotUpdateTranscodingProfile()1333 void KdenliveSettingsDialog::slotUpdateTranscodingProfile()
1334 {
1335 QListWidgetItem *item = m_configTranscode.profiles_list->currentItem();
1336 if (!item) {
1337 return;
1338 }
1339 m_configTranscode.button_update->setEnabled(false);
1340 QString profilestr = m_configTranscode.profile_parameters->toPlainText();
1341 profilestr.append(" %1." + m_configTranscode.profile_extension->text());
1342 profilestr.append(';');
1343 if (!m_configTranscode.profile_description->text().isEmpty()) {
1344 profilestr.append(m_configTranscode.profile_description->text());
1345 }
1346 if (m_configTranscode.profile_audioonly->isChecked()) {
1347 profilestr.append(QStringLiteral(";audio"));
1348 }
1349 item->setData(Qt::UserRole, profilestr);
1350 slotDialogModified();
1351 }
1352
slotDeleteTranscode()1353 void KdenliveSettingsDialog::slotDeleteTranscode()
1354 {
1355 QListWidgetItem *item = m_configTranscode.profiles_list->currentItem();
1356 if (item == nullptr) {
1357 return;
1358 }
1359 delete item;
1360 slotDialogModified();
1361 }
1362
slotEnableTranscodeUpdate()1363 void KdenliveSettingsDialog::slotEnableTranscodeUpdate()
1364 {
1365 if (!m_configTranscode.profile_box->isEnabled()) {
1366 return;
1367 }
1368 bool allow = true;
1369 if (m_configTranscode.profile_extension->text().isEmpty()) {
1370 allow = false;
1371 }
1372 m_configTranscode.button_update->setEnabled(allow);
1373 }
1374
slotSetTranscodeProfile()1375 void KdenliveSettingsDialog::slotSetTranscodeProfile()
1376 {
1377 m_configTranscode.profile_box->setEnabled(false);
1378 m_configTranscode.button_update->setEnabled(false);
1379 m_configTranscode.profile_description->clear();
1380 m_configTranscode.profile_extension->clear();
1381 m_configTranscode.profile_parameters->clear();
1382 m_configTranscode.profile_audioonly->setChecked(false);
1383 QListWidgetItem *item = m_configTranscode.profiles_list->currentItem();
1384 if (!item) {
1385 return;
1386 }
1387 QString profilestr = item->data(Qt::UserRole).toString();
1388 if (profilestr.contains(QLatin1Char(';'))) {
1389 m_configTranscode.profile_description->setText(profilestr.section(QLatin1Char(';'), 1, 1));
1390 if (profilestr.section(QLatin1Char(';'), 2, 2) == QLatin1String("audio")) {
1391 m_configTranscode.profile_audioonly->setChecked(true);
1392 }
1393 profilestr = profilestr.section(QLatin1Char(';'), 0, 0).simplified();
1394 }
1395 m_configTranscode.profile_extension->setText(profilestr.section(QLatin1Char('.'), -1));
1396 m_configTranscode.profile_parameters->setPlainText(profilestr.section(QLatin1Char(' '), 0, -2));
1397 m_configTranscode.profile_box->setEnabled(true);
1398 }
1399
slotShuttleModified()1400 void KdenliveSettingsDialog::slotShuttleModified()
1401 {
1402 #ifdef USE_JOGSHUTTLE
1403 QStringList actions;
1404 actions << QStringLiteral("monitor_pause"); // the Job rest position action.
1405 for (QComboBox *button : qAsConst(m_shuttle_buttons)) {
1406 actions << m_mappable_actions[button->currentText()];
1407 }
1408 QString maps = JogShuttleConfig::actionMap(actions);
1409 m_shuttleModified = KdenliveSettings::shuttlebuttons() != maps;
1410 #endif
1411 KConfigDialog::updateButtons();
1412 }
1413
slotDialogModified()1414 void KdenliveSettingsDialog::slotDialogModified()
1415 {
1416 m_modified = true;
1417 KConfigDialog::updateButtons();
1418 }
1419
1420 // virtual
hasChanged()1421 bool KdenliveSettingsDialog::hasChanged()
1422 {
1423 if (m_modified || m_shuttleModified) {
1424 return true;
1425 }
1426 return KConfigDialog::hasChanged();
1427 }
1428
slotUpdatev4lDevice()1429 void KdenliveSettingsDialog::slotUpdatev4lDevice()
1430 {
1431 QString device = m_configCapture.kcfg_detectedv4ldevices->itemData(m_configCapture.kcfg_detectedv4ldevices->currentIndex()).toString();
1432 if (!device.isEmpty()) {
1433 m_configCapture.kcfg_video4vdevice->setText(device);
1434 }
1435 QString info = m_configCapture.kcfg_detectedv4ldevices->itemData(m_configCapture.kcfg_detectedv4ldevices->currentIndex(), Qt::UserRole + 1).toString();
1436
1437 m_configCapture.kcfg_v4l_format->blockSignals(true);
1438 m_configCapture.kcfg_v4l_format->clear();
1439
1440 QString vl4ProfilePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/video4linux");
1441 if (QFile::exists(vl4ProfilePath)) {
1442 m_configCapture.kcfg_v4l_format->addItem(i18n("Current settings"));
1443 }
1444
1445 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1446 QStringList pixelformats = info.split('>', QString::SkipEmptyParts);
1447 #else
1448 QStringList pixelformats = info.split('>', Qt::SkipEmptyParts);
1449 #endif
1450 QString itemSize;
1451 QString pixelFormat;
1452 QStringList itemRates;
1453 for (int i = 0; i < pixelformats.count(); ++i) {
1454 QString format = pixelformats.at(i).section(QLatin1Char(':'), 0, 0);
1455 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1456 QStringList sizes = pixelformats.at(i).split(':', QString::SkipEmptyParts);
1457 #else
1458 QStringList sizes = pixelformats.at(i).split(':', Qt::SkipEmptyParts);
1459 #endif
1460 pixelFormat = sizes.takeFirst();
1461 for (int j = 0; j < sizes.count(); ++j) {
1462 itemSize = sizes.at(j).section(QLatin1Char('='), 0, 0);
1463 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1464 itemRates = sizes.at(j).section(QLatin1Char('='), 1, 1).split(QLatin1Char(','), QString::SkipEmptyParts);
1465 #else
1466 itemRates = sizes.at(j).section(QLatin1Char('='), 1, 1).split(QLatin1Char(','), Qt::SkipEmptyParts);
1467 #endif
1468 for (int k = 0; k < itemRates.count(); ++k) {
1469 m_configCapture.kcfg_v4l_format->addItem(
1470 QLatin1Char('[') + format + QStringLiteral("] ") + itemSize + QStringLiteral(" (") + itemRates.at(k) + QLatin1Char(')'),
1471 QStringList() << format << itemSize.section('x', 0, 0) << itemSize.section('x', 1, 1) << itemRates.at(k).section(QLatin1Char('/'), 0, 0)
1472 << itemRates.at(k).section(QLatin1Char('/'), 1, 1));
1473 }
1474 }
1475 }
1476 m_configCapture.kcfg_v4l_format->blockSignals(false);
1477 slotUpdatev4lCaptureProfile();
1478 }
1479
slotUpdatev4lCaptureProfile()1480 void KdenliveSettingsDialog::slotUpdatev4lCaptureProfile()
1481 {
1482 QStringList info = m_configCapture.kcfg_v4l_format->itemData(m_configCapture.kcfg_v4l_format->currentIndex(), Qt::UserRole).toStringList();
1483 if (info.isEmpty()) {
1484 // No auto info, display the current ones
1485 loadCurrentV4lProfileInfo();
1486 return;
1487 }
1488 m_configCapture.p_size->setText(info.at(1) + QLatin1Char('x') + info.at(2));
1489 m_configCapture.p_fps->setText(info.at(3) + QLatin1Char('/') + info.at(4));
1490 m_configCapture.p_aspect->setText(QStringLiteral("1/1"));
1491 m_configCapture.p_display->setText(info.at(1) + QLatin1Char('/') + info.at(2));
1492 m_configCapture.p_colorspace->setText(ProfileRepository::getColorspaceDescription(601));
1493 m_configCapture.p_progressive->setText(i18n("Progressive"));
1494
1495 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
1496 if (!dir.exists() || !dir.exists(QStringLiteral("video4linux"))) {
1497 saveCurrentV4lProfile();
1498 }
1499 }
1500
loadCurrentV4lProfileInfo()1501 void KdenliveSettingsDialog::loadCurrentV4lProfileInfo()
1502 {
1503 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
1504 if (!dir.exists()) {
1505 dir.mkpath(QStringLiteral("."));
1506 }
1507 if (!ProfileRepository::get()->profileExists(dir.absoluteFilePath(QStringLiteral("video4linux")))) {
1508 // No default formats found, build one
1509 std::unique_ptr<ProfileParam> prof(new ProfileParam(pCore->getCurrentProfile().get()));
1510 prof->m_width = 320;
1511 prof->m_height = 200;
1512 prof->m_frame_rate_num = 15;
1513 prof->m_frame_rate_den = 1;
1514 prof->m_display_aspect_num = 4;
1515 prof->m_display_aspect_den = 3;
1516 prof->m_sample_aspect_num = 1;
1517 prof->m_sample_aspect_den = 1;
1518 prof->m_progressive = true;
1519 prof->m_colorspace = 601;
1520 ProfileRepository::get()->saveProfile(prof.get(), dir.absoluteFilePath(QStringLiteral("video4linux")));
1521 }
1522 auto &prof = ProfileRepository::get()->getProfile(dir.absoluteFilePath(QStringLiteral("video4linux")));
1523 m_configCapture.p_size->setText(QString::number(prof->width()) + QLatin1Char('x') + QString::number(prof->height()));
1524 m_configCapture.p_fps->setText(QString::number(prof->frame_rate_num()) + QLatin1Char('/') + QString::number(prof->frame_rate_den()));
1525 m_configCapture.p_aspect->setText(QString::number(prof->sample_aspect_num()) + QLatin1Char('/') + QString::number(prof->sample_aspect_den()));
1526 m_configCapture.p_display->setText(QString::number(prof->display_aspect_num()) + QLatin1Char('/') + QString::number(prof->display_aspect_den()));
1527 m_configCapture.p_colorspace->setText(ProfileRepository::getColorspaceDescription(prof->colorspace()));
1528 if (prof->progressive()) {
1529 m_configCapture.p_progressive->setText(i18n("Progressive"));
1530 }
1531 }
1532
saveCurrentV4lProfile()1533 void KdenliveSettingsDialog::saveCurrentV4lProfile()
1534 {
1535 std::unique_ptr<ProfileParam> profile(new ProfileParam(pCore->getCurrentProfile().get()));
1536 profile->m_description = QStringLiteral("Video4Linux capture");
1537 profile->m_colorspace = ProfileRepository::getColorspaceFromDescription(m_configCapture.p_colorspace->text());
1538 profile->m_width = m_configCapture.p_size->text().section('x', 0, 0).toInt();
1539 profile->m_height = m_configCapture.p_size->text().section('x', 1, 1).toInt();
1540 profile->m_sample_aspect_num = m_configCapture.p_aspect->text().section(QLatin1Char('/'), 0, 0).toInt();
1541 profile->m_sample_aspect_den = m_configCapture.p_aspect->text().section(QLatin1Char('/'), 1, 1).toInt();
1542 profile->m_display_aspect_num = m_configCapture.p_display->text().section(QLatin1Char('/'), 0, 0).toInt();
1543 profile->m_display_aspect_den = m_configCapture.p_display->text().section(QLatin1Char('/'), 1, 1).toInt();
1544 profile->m_frame_rate_num = m_configCapture.p_fps->text().section(QLatin1Char('/'), 0, 0).toInt();
1545 profile->m_frame_rate_den = m_configCapture.p_fps->text().section(QLatin1Char('/'), 1, 1).toInt();
1546 profile->m_progressive = m_configCapture.p_progressive->text() == i18n("Progressive");
1547 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
1548 if (!dir.exists()) {
1549 dir.mkpath(QStringLiteral("."));
1550 }
1551 ProfileRepository::get()->saveProfile(profile.get(), dir.absoluteFilePath(QStringLiteral("video4linux")));
1552 }
1553
slotManageEncodingProfile()1554 void KdenliveSettingsDialog::slotManageEncodingProfile()
1555 {
1556 auto *act = qobject_cast<QAction *>(sender());
1557 int type = 0;
1558 if (act) {
1559 type = act->data().toInt();
1560 }
1561 QPointer<EncodingProfilesDialog> dia = new EncodingProfilesDialog(type);
1562 dia->exec();
1563 delete dia;
1564 loadEncodingProfiles();
1565 }
1566
loadExternalProxyProfiles()1567 void KdenliveSettingsDialog::loadExternalProxyProfiles()
1568 {
1569 // load proxy profiles
1570 KConfig conf(QStringLiteral("externalproxies.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
1571 KConfigGroup group(&conf, "proxy");
1572 QMap<QString, QString> values = group.entryMap();
1573 QMapIterator<QString, QString> k(values);
1574 QString currentItem = KdenliveSettings::externalProxyProfile();
1575 m_configProxy.kcfg_external_proxy_profile->blockSignals(true);
1576 m_configProxy.kcfg_external_proxy_profile->clear();
1577 while (k.hasNext()) {
1578 k.next();
1579 if (!k.key().isEmpty()) {
1580 if (k.value().contains(QLatin1Char(';'))) {
1581 m_configProxy.kcfg_external_proxy_profile->addItem(k.key(), k.value());
1582 }
1583 }
1584 }
1585 if (!currentItem.isEmpty()) {
1586 m_configProxy.kcfg_external_proxy_profile->setCurrentIndex(m_configProxy.kcfg_external_proxy_profile->findText(currentItem));
1587 }
1588 m_configProxy.kcfg_external_proxy_profile->blockSignals(false);
1589 }
1590
loadEncodingProfiles()1591 void KdenliveSettingsDialog::loadEncodingProfiles()
1592 {
1593 KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
1594
1595 // Load v4l profiles
1596 m_configCapture.kcfg_v4l_profile->blockSignals(true);
1597 QString currentItem = m_configCapture.kcfg_v4l_profile->currentText();
1598 m_configCapture.kcfg_v4l_profile->clear();
1599 KConfigGroup group(&conf, "video4linux");
1600 QMap<QString, QString> values = group.entryMap();
1601 QMapIterator<QString, QString> i(values);
1602 while (i.hasNext()) {
1603 i.next();
1604 if (!i.key().isEmpty()) {
1605 m_configCapture.kcfg_v4l_profile->addItem(i.key(), i.value());
1606 }
1607 }
1608 m_configCapture.kcfg_v4l_profile->blockSignals(false);
1609 if (!currentItem.isEmpty()) {
1610 m_configCapture.kcfg_v4l_profile->setCurrentIndex(m_configCapture.kcfg_v4l_profile->findText(currentItem));
1611 }
1612
1613 // Load Screen Grab profiles
1614 m_configCapture.kcfg_grab_profile->blockSignals(true);
1615 currentItem = m_configCapture.kcfg_grab_profile->currentText();
1616 m_configCapture.kcfg_grab_profile->clear();
1617 KConfigGroup group2(&conf, "screengrab");
1618 values = group2.entryMap();
1619 QMapIterator<QString, QString> j(values);
1620 while (j.hasNext()) {
1621 j.next();
1622 if (!j.key().isEmpty()) {
1623 m_configCapture.kcfg_grab_profile->addItem(j.key(), j.value());
1624 }
1625 }
1626 m_configCapture.kcfg_grab_profile->blockSignals(false);
1627 if (!currentItem.isEmpty()) {
1628 m_configCapture.kcfg_grab_profile->setCurrentIndex(m_configCapture.kcfg_grab_profile->findText(currentItem));
1629 }
1630
1631 // Load Decklink profiles
1632 m_configCapture.kcfg_decklink_profile->blockSignals(true);
1633 currentItem = m_configCapture.kcfg_decklink_profile->currentText();
1634 m_configCapture.kcfg_decklink_profile->clear();
1635 KConfigGroup group3(&conf, "decklink");
1636 values = group3.entryMap();
1637 QMapIterator<QString, QString> k(values);
1638 while (k.hasNext()) {
1639 k.next();
1640 if (!k.key().isEmpty()) {
1641 m_configCapture.kcfg_decklink_profile->addItem(k.key(), k.value());
1642 }
1643 }
1644 m_configCapture.kcfg_decklink_profile->blockSignals(false);
1645 if (!currentItem.isEmpty()) {
1646 m_configCapture.kcfg_decklink_profile->setCurrentIndex(m_configCapture.kcfg_decklink_profile->findText(currentItem));
1647 }
1648
1649 // Load Timeline Preview profiles
1650 m_configProject.kcfg_preview_profile->blockSignals(true);
1651 currentItem = m_configProject.kcfg_preview_profile->currentText();
1652 m_configProject.kcfg_preview_profile->clear();
1653 KConfigGroup group5(&conf, "timelinepreview");
1654 values = group5.entryMap();
1655 m_configProject.kcfg_preview_profile->addItem(i18n("Automatic"));
1656 QMapIterator<QString, QString> l(values);
1657 while (l.hasNext()) {
1658 l.next();
1659 if (!l.key().isEmpty()) {
1660 m_configProject.kcfg_preview_profile->addItem(l.key(), l.value());
1661 }
1662 }
1663 if (!currentItem.isEmpty()) {
1664 m_configProject.kcfg_preview_profile->setCurrentIndex(m_configProject.kcfg_preview_profile->findText(currentItem));
1665 }
1666 m_configProject.kcfg_preview_profile->blockSignals(false);
1667 QString profilestr = m_configProject.kcfg_preview_profile->itemData(m_configProject.kcfg_preview_profile->currentIndex()).toString();
1668 if (profilestr.isEmpty()) {
1669 m_configProject.previewparams->clear();
1670 } else {
1671 m_configProject.previewparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
1672 }
1673
1674 // Load Proxy profiles
1675 m_configProxy.kcfg_proxy_profile->blockSignals(true);
1676 currentItem = m_configProxy.kcfg_proxy_profile->currentText();
1677 m_configProxy.kcfg_proxy_profile->clear();
1678 KConfigGroup group4(&conf, "proxy");
1679 values = group4.entryMap();
1680 m_configProxy.kcfg_proxy_profile->addItem(i18n("Automatic"));
1681 QMapIterator<QString, QString> m(values);
1682 while (m.hasNext()) {
1683 m.next();
1684 if (!m.key().isEmpty()) {
1685 m_configProxy.kcfg_proxy_profile->addItem(m.key(), m.value());
1686 }
1687 }
1688 if (!currentItem.isEmpty()) {
1689 m_configProxy.kcfg_proxy_profile->setCurrentIndex(m_configProxy.kcfg_proxy_profile->findText(currentItem));
1690 }
1691 m_configProxy.kcfg_proxy_profile->blockSignals(false);
1692 profilestr = m_configProxy.kcfg_proxy_profile->itemData(m_configProxy.kcfg_proxy_profile->currentIndex()).toString();
1693 if (profilestr.isEmpty()) {
1694 m_configProxy.proxyparams->clear();
1695 } else {
1696 m_configProxy.proxyparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
1697 }
1698 }
1699
slotUpdateDecklinkProfile(int ix)1700 void KdenliveSettingsDialog::slotUpdateDecklinkProfile(int ix)
1701 {
1702 if (ix == -1) {
1703 ix = KdenliveSettings::decklink_profile();
1704 } else {
1705 ix = m_configCapture.kcfg_decklink_profile->currentIndex();
1706 }
1707 QString profilestr = m_configCapture.kcfg_decklink_profile->itemData(ix).toString();
1708 if (profilestr.isEmpty()) {
1709 return;
1710 }
1711 m_configCapture.decklink_parameters->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
1712 //
1713 }
1714
slotUpdateV4lProfile(int ix)1715 void KdenliveSettingsDialog::slotUpdateV4lProfile(int ix)
1716 {
1717 if (ix == -1) {
1718 ix = KdenliveSettings::v4l_profile();
1719 } else {
1720 ix = m_configCapture.kcfg_v4l_profile->currentIndex();
1721 }
1722 QString profilestr = m_configCapture.kcfg_v4l_profile->itemData(ix).toString();
1723 if (profilestr.isEmpty()) {
1724 return;
1725 }
1726 m_configCapture.v4l_parameters->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
1727 //
1728 }
1729
slotUpdateGrabProfile(int ix)1730 void KdenliveSettingsDialog::slotUpdateGrabProfile(int ix)
1731 {
1732 if (ix == -1) {
1733 ix = KdenliveSettings::grab_profile();
1734 } else {
1735 ix = m_configCapture.kcfg_grab_profile->currentIndex();
1736 }
1737 QString profilestr = m_configCapture.kcfg_grab_profile->itemData(ix).toString();
1738 if (profilestr.isEmpty()) {
1739 return;
1740 }
1741 m_configCapture.grab_parameters->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
1742 //
1743 }
1744
slotUpdateProxyProfile(int ix)1745 void KdenliveSettingsDialog::slotUpdateProxyProfile(int ix)
1746 {
1747 if (ix == -1) {
1748 ix = KdenliveSettings::proxy_profile();
1749 } else {
1750 ix = m_configProxy.kcfg_proxy_profile->currentIndex();
1751 }
1752 QString profilestr = m_configProxy.kcfg_proxy_profile->itemData(ix).toString();
1753 if (profilestr.isEmpty()) {
1754 return;
1755 }
1756 m_configProxy.proxyparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
1757 }
1758
slotUpdatePreviewProfile(int ix)1759 void KdenliveSettingsDialog::slotUpdatePreviewProfile(int ix)
1760 {
1761 if (ix == -1) {
1762 ix = KdenliveSettings::preview_profile();
1763 } else {
1764 ix = m_configProject.kcfg_preview_profile->currentIndex();
1765 }
1766 QString profilestr = m_configProject.kcfg_preview_profile->itemData(ix).toString();
1767 if (profilestr.isEmpty()) {
1768 return;
1769 }
1770 m_configProject.previewparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
1771 }
1772
slotEditVideo4LinuxProfile()1773 void KdenliveSettingsDialog::slotEditVideo4LinuxProfile()
1774 {
1775 QString vl4ProfilePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/video4linux");
1776 QPointer<ProfilesDialog> w = new ProfilesDialog(vl4ProfilePath, true);
1777 if (w->exec() == QDialog::Accepted) {
1778 // save and update profile
1779 loadCurrentV4lProfileInfo();
1780 }
1781 delete w;
1782 }
1783
slotReloadBlackMagic()1784 void KdenliveSettingsDialog::slotReloadBlackMagic()
1785 {
1786 getBlackMagicDeviceList(m_configCapture.kcfg_decklink_capturedevice, true);
1787 if (!getBlackMagicOutputDeviceList(m_configSdl.kcfg_blackmagic_output_device, true)) {
1788 // No blackmagic card found
1789 m_configSdl.kcfg_external_display->setEnabled(false);
1790 }
1791 m_configSdl.kcfg_external_display->setEnabled(KdenliveSettings::decklink_device_found());
1792 }
1793
checkProfile()1794 void KdenliveSettingsDialog::checkProfile()
1795 {
1796 m_pw->loadProfile(KdenliveSettings::default_profile().isEmpty() ? pCore->getCurrentProfile()->path() : KdenliveSettings::default_profile());
1797 }
1798
slotReloadShuttleDevices()1799 void KdenliveSettingsDialog::slotReloadShuttleDevices()
1800 {
1801 #ifdef USE_JOGSHUTTLE
1802 QString devDirStr = QStringLiteral("/dev/input/by-id");
1803 QDir devDir(devDirStr);
1804 if (!devDir.exists()) {
1805 devDirStr = QStringLiteral("/dev/input");
1806 }
1807
1808 QStringList devNamesList;
1809 QStringList devPathList;
1810 m_configShuttle.shuttledevicelist->clear();
1811
1812 DeviceMap devMap = JogShuttle::enumerateDevices(devDirStr);
1813 DeviceMapIter iter = devMap.begin();
1814 while (iter != devMap.end()) {
1815 m_configShuttle.shuttledevicelist->addItem(iter.key(), iter.value());
1816 devNamesList << iter.key();
1817 devPathList << iter.value();
1818 ++iter;
1819 }
1820 KdenliveSettings::setShuttledevicenames(devNamesList);
1821 KdenliveSettings::setShuttledevicepaths(devPathList);
1822 QTimer::singleShot(200, this, SLOT(slotUpdateShuttleDevice()));
1823 #endif // USE_JOGSHUTTLE
1824 }
1825
slotUpdateAudioCaptureChannels(int index)1826 void KdenliveSettingsDialog::slotUpdateAudioCaptureChannels(int index)
1827 {
1828 KdenliveSettings::setAudiocapturechannels(m_configCapture.audiocapturechannels->itemData(index).toInt());
1829 }
1830
slotUpdateAudioCaptureSampleRate(int index)1831 void KdenliveSettingsDialog::slotUpdateAudioCaptureSampleRate(int index)
1832 {
1833 KdenliveSettings::setAudiocapturesamplerate(m_configCapture.audiocapturesamplerate->itemData(index).toInt());
1834 }
1835
initSpeechPage()1836 void KdenliveSettingsDialog::initSpeechPage()
1837 {
1838 m_voskAction = new QAction(i18n("Install missing dependencies"), this);
1839 m_speechListWidget = new SpeechList(this);
1840 connect(m_speechListWidget, &SpeechList::getDictionary, this, &KdenliveSettingsDialog::getDictionary);
1841 QVBoxLayout *l = new QVBoxLayout(m_configSpeech.list_frame);
1842 l->setContentsMargins(0, 0, 0, 0);
1843 l->addWidget(m_speechListWidget);
1844 m_configSpeech.speech_info->setWordWrap(true);
1845 m_configSpeech.check_vosk->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
1846 m_configSpeech.check_vosk->setToolTip(i18n("Check VOSK installation"));
1847 connect(m_configSpeech.check_vosk, &QPushButton::clicked, this, [this]() {
1848 m_configSpeech.check_vosk->setEnabled(false);
1849 KdenliveSettings::setVosk_found(false);
1850 KdenliveSettings::setVosk_srt_found(false);
1851 checkVoskDependencies();
1852 m_configSpeech.check_vosk->setEnabled(true);
1853 });
1854 connect(this, &KdenliveSettingsDialog::showSpeechMessage, this, &KdenliveSettingsDialog::doShowSpeechMessage);
1855 connect(m_voskAction, &QAction::triggered, this, [this]() {
1856 #ifdef Q_OS_WIN
1857 QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python"));
1858 #else
1859 QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python3"));
1860 #endif
1861 if (pyExec.isEmpty()) {
1862 doShowSpeechMessage(i18n("Please install python3 and pip on your system."), KMessageWidget::Warning);
1863 return;
1864 }
1865 QString speechScript = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("scripts/checkvosk.py"));
1866 if (speechScript.isEmpty()) {
1867 doShowSpeechMessage(i18n("The speech script was not found, check your install."), KMessageWidget::Warning);
1868 return;
1869 }
1870 QProcess checkJob;
1871 if (!KdenliveSettings::vosk_found() || !KdenliveSettings::vosk_srt_found()) {
1872 m_voskAction->setEnabled(false);
1873 doShowSpeechMessage(i18n("Installing modules..."), KMessageWidget::Information);
1874 qApp->processEvents();
1875 checkJob.start(pyExec, {speechScript, QStringLiteral("install")});
1876 checkJob.waitForFinished();
1877 checkVoskDependencies();
1878 } else {
1879 // upgrade
1880 m_voskUpdated = true;
1881 m_voskAction->setEnabled(false);
1882 doShowSpeechMessage(i18n("Updating modules..."), KMessageWidget::Information);
1883 qApp->processEvents();
1884 checkJob.start(pyExec, {speechScript, QStringLiteral("upgrade")});
1885 checkJob.waitForFinished();
1886 m_configSpeech.speech_info->removeAction(m_voskAction);
1887 checkVoskVersion(pyExec);
1888 }
1889 });
1890 checkVoskDependencies();
1891 connect(m_configSpeech.custom_vosk_folder, &QCheckBox::stateChanged, this, [this](int state) {
1892 m_configSpeech.vosk_folder->setEnabled(state != Qt::Unchecked);
1893 if (state == Qt::Unchecked) {
1894 // Clear custom folder
1895 m_configSpeech.vosk_folder->clear();
1896 KdenliveSettings::setVosk_folder_path(QString());
1897 slotParseVoskDictionaries();
1898 }
1899 });
1900 connect(m_configSpeech.vosk_folder, &KUrlRequester::urlSelected, this, [this](QUrl url) {
1901 KdenliveSettings::setVosk_folder_path(url.toLocalFile());
1902 slotParseVoskDictionaries();
1903 });
1904 m_configSpeech.models_url->setText(i18n("Download speech models from: <a href=\"https://alphacephei.com/vosk/models\">https://alphacephei.com/vosk/models</a>"));
1905 connect(m_configSpeech.models_url, &QLabel::linkActivated, this, [&](const QString &contents) {
1906 qDebug()<<"=== LINK CLICKED: "<<contents;
1907 #if KIO_VERSION > QT_VERSION_CHECK(5, 70, 0)
1908 auto *job = new KIO::OpenUrlJob(QUrl(contents));
1909 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
1910 // methods like setRunExecutables, setSuggestedFilename, setEnableExternalBrowser, setFollowRedirections
1911 // exist in both classes
1912 job->start();
1913 #else
1914 new KRun(QUrl(contents), this);
1915 #endif
1916 });
1917 m_configSpeech.button_add->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
1918 m_configSpeech.button_delete->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
1919 m_configSpeech.button_add->setToolTip(i18n("Add a new speech model from an archive file"));
1920 m_configSpeech.button_delete->setToolTip(i18n("Delete the selected speech model"));
1921 connect(m_configSpeech.button_add, &QToolButton::clicked, this, [this] () {
1922 this->getDictionary();
1923 });
1924 connect(m_configSpeech.button_delete, &QToolButton::clicked, this, &KdenliveSettingsDialog::removeDictionary);
1925 connect(this, &KdenliveSettingsDialog::parseDictionaries, this, &KdenliveSettingsDialog::slotParseVoskDictionaries);
1926 slotParseVoskDictionaries();
1927 }
1928
checkVoskDependencies()1929 void KdenliveSettingsDialog::checkVoskDependencies()
1930 {
1931 #ifdef Q_OS_WIN
1932 QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python"));
1933 QString pip3Exec = QStandardPaths::findExecutable(QStringLiteral("pip"));
1934 #else
1935 QString pyExec = QStandardPaths::findExecutable(QStringLiteral("python3"));
1936 QString pip3Exec = QStandardPaths::findExecutable(QStringLiteral("pip3"));
1937 #endif
1938 if (pyExec.isEmpty()) {
1939 doShowSpeechMessage(i18n("Cannot find python3, please install it on your system."), KMessageWidget::Warning);
1940 return;
1941 } else if (!KdenliveSettings::vosk_found() || !KdenliveSettings::vosk_srt_found()) {
1942 QProcess checkJob;
1943 QString speechScript = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("scripts/checkvosk.py"));
1944 if (speechScript.isEmpty()) {
1945 doShowSpeechMessage(i18n("The speech script was not found, check your install."), KMessageWidget::Warning);
1946 return;
1947 } else {
1948 m_configSpeech.speech_info->removeAction(m_voskAction);
1949 checkJob.start(pyExec, {speechScript});
1950 checkJob.waitForFinished();
1951 QString output = checkJob.readAllStandardOutput();
1952 QString missingModules;
1953 if (!output.contains(QLatin1String("vosk"))) {
1954 KdenliveSettings::setVosk_found(true);
1955 } else {
1956 KdenliveSettings::setVosk_found(false);
1957 missingModules = i18n("The VOSK python module is required for speech features. ");
1958 }
1959 if (!output.contains(QLatin1String("srt"))) {
1960 KdenliveSettings::setVosk_srt_found(true);
1961 } else {
1962 KdenliveSettings::setVosk_srt_found(false);
1963 missingModules.append(i18n("The SRT python module is required for automated subtitling."));
1964 }
1965 if (!missingModules.isEmpty()) {
1966 m_voskAction->setEnabled(true);
1967 m_voskAction->setText(i18n("Install missing dependencies"));
1968 m_configSpeech.speech_info->addAction(m_voskAction);
1969 doShowSpeechMessage(missingModules, KMessageWidget::Warning);
1970 } else {
1971 if (m_speechListWidget->count() == 0) {
1972 doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
1973 } else if (!pip3Exec.isEmpty()) {
1974 if (!m_voskUpdated) {
1975 // only allow upgrading python modules once
1976 m_voskAction->setText(i18n("Check for update"));
1977 m_configSpeech.speech_info->addAction(m_voskAction);
1978 }
1979 QtConcurrent::run(this, &KdenliveSettingsDialog::checkVoskVersion, pyExec);
1980 }
1981 }
1982 emit pCore->updateVoskAvailability();
1983 }
1984 } else {
1985 if (m_speechListWidget->count() == 0) {
1986 doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
1987 } else if (!pip3Exec.isEmpty()) {
1988 if (!m_voskUpdated) {
1989 // only allow upgrading python modules once
1990 m_voskAction->setText(i18n("Check for update"));
1991 m_configSpeech.speech_info->addAction(m_voskAction);
1992 }
1993 QtConcurrent::run(this, &KdenliveSettingsDialog::checkVoskVersion, pyExec);
1994 }
1995 }
1996 }
1997
checkVoskVersion(const QString pyExec)1998 void KdenliveSettingsDialog::checkVoskVersion(const QString pyExec)
1999 {
2000 emit showSpeechMessage(i18n("Checking configuration..."), KMessageWidget::Information);
2001 QString speechScript = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("scripts/checkvosk.py"));
2002 QProcess checkJob;
2003 checkJob.start(pyExec, {speechScript, QStringLiteral("check")});
2004 checkJob.waitForFinished();
2005 QString message;
2006 QString output = checkJob.readAllStandardOutput();
2007 QStringList versions = output.split(QStringLiteral("Version: "));
2008 if (versions.count() >= 3) {
2009 bool voskFirst = versions.takeFirst().contains(QLatin1String("vosk"));
2010 QString voskVersion;
2011 QString srtVersion;
2012 QString cut = versions.takeFirst();
2013 if (voskFirst) {
2014 voskVersion = cut.simplified().section(QLatin1Char(' '), 0, 0);
2015 cut = versions.takeFirst();
2016 srtVersion = cut.simplified().section(QLatin1Char(' '), 0, 0);
2017 } else {
2018 srtVersion = cut.simplified().section(QLatin1Char(' '), 0, 0);
2019 cut = versions.takeFirst();
2020 voskVersion = cut.simplified().section(QLatin1Char(' '), 0, 0);
2021 }
2022 message = i18n("Speech to text is configured. Vosk %1, Srt %2", voskVersion, srtVersion);
2023 } else {
2024 message = i18n("Speech to text is properly configured.");
2025 }
2026 emit showSpeechMessage(message, KMessageWidget::Positive);
2027 }
2028
doShowSpeechMessage(const QString & message,int messageType)2029 void KdenliveSettingsDialog::doShowSpeechMessage(const QString &message, int messageType)
2030 {
2031 m_configSpeech.speech_info->setMessageType(static_cast<KMessageWidget::MessageType>(messageType));
2032 m_configSpeech.speech_info->setText(message);
2033 m_configSpeech.speech_info->animatedShow();
2034 }
2035
getDictionary(const QUrl sourceUrl)2036 void KdenliveSettingsDialog::getDictionary(const QUrl sourceUrl)
2037 {
2038 QUrl url = KUrlRequesterDialog::getUrl(sourceUrl, this, i18n("Enter url for the new dictionary"));
2039 if (url.isEmpty()) {
2040 return;
2041 }
2042 if (!url.isLocalFile()) {
2043 KIO::FileCopyJob *copyjob = KIO::file_copy(url, QUrl::fromLocalFile(QDir::temp().absoluteFilePath(url.fileName())));
2044 doShowSpeechMessage(i18n("Downloading model..."), KMessageWidget::Information);
2045 connect(copyjob, &KIO::FileCopyJob::result, this, &KdenliveSettingsDialog::downloadModelFinished);
2046 } else {
2047 processArchive(url.toLocalFile());
2048 }
2049
2050 }
2051
removeDictionary()2052 void KdenliveSettingsDialog::removeDictionary()
2053 {
2054 if (!KdenliveSettings::vosk_folder_path().isEmpty()) {
2055 doShowSpeechMessage(i18n("We do not allow deleting custom folder models, please do it manually."), KMessageWidget::Warning);
2056 return;
2057 }
2058 QString modelDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
2059 QDir dir(modelDirectory);
2060 if (!dir.cd(QStringLiteral("speechmodels"))) {
2061 doShowSpeechMessage(i18n("Cannot access dictionary folder."), KMessageWidget::Warning);
2062 return;
2063 }
2064
2065 if (!m_speechListWidget->currentItem()) {
2066 return;
2067 }
2068 QString currentModel = m_speechListWidget->currentItem()->text();
2069 if (!currentModel.isEmpty() && dir.cd(currentModel)) {
2070 if (KMessageBox::questionYesNo(this, i18n("Delete folder:\n%1", dir.absolutePath())) == KMessageBox::Yes) {
2071 // Make sure we don't accidentally delete a folder that is not ours
2072 if (dir.absolutePath().contains(QLatin1String("speechmodels"))) {
2073 dir.removeRecursively();
2074 slotParseVoskDictionaries();
2075 }
2076 }
2077 }
2078 }
2079
downloadModelFinished(KJob * job)2080 void KdenliveSettingsDialog::downloadModelFinished(KJob* job)
2081 {
2082 qDebug()<<"=== DOWNLOAD FINISHED!!";
2083 if (job->error() == 0 || job->error() == 112) {
2084 qDebug()<<"=== NO ERROR ON DWNLD!!";
2085 auto *jb = static_cast<KIO::FileCopyJob*>(job);
2086 if (jb) {
2087 qDebug()<<"=== JOB FOUND!!";
2088 QString archiveFile = jb->destUrl().toLocalFile();
2089 processArchive(archiveFile);
2090 } else {
2091 qDebug()<<"=== JOB NOT FOUND!!";
2092 m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
2093 m_configSpeech.speech_info->setText(i18n("Download error"));
2094 }
2095 } else {
2096 qDebug()<<"=== GOT JOB ERROR: "<<job->error();
2097 m_configSpeech.speech_info->setMessageType(KMessageWidget::Warning);
2098 m_configSpeech.speech_info->setText(i18n("Download error %1", job->errorString()));
2099 }
2100 }
2101
processArchive(const QString archiveFile)2102 void KdenliveSettingsDialog::processArchive(const QString archiveFile)
2103 {
2104 QMimeDatabase db;
2105 QMimeType type = db.mimeTypeForFile(archiveFile);
2106 std::unique_ptr<KArchive> archive;
2107 if (type.inherits(QStringLiteral("application/zip"))) {
2108 archive = std::make_unique<KZip>(archiveFile);
2109 } else {
2110 archive = std::make_unique<KTar>(archiveFile);
2111 }
2112 QString modelDirectory = KdenliveSettings::vosk_folder_path();
2113 QDir dir;
2114 if (modelDirectory.isEmpty()) {
2115 modelDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
2116 dir = QDir(modelDirectory);
2117 dir.mkdir(QStringLiteral("speechmodels"));
2118 if (!dir.cd(QStringLiteral("speechmodels"))) {
2119 doShowSpeechMessage(i18n("Cannot access dictionary folder."), KMessageWidget::Warning);
2120 return;
2121 }
2122 } else {
2123 dir = QDir(modelDirectory);
2124 }
2125 if (archive->open(QIODevice::ReadOnly)) {
2126 doShowSpeechMessage(i18n("Extracting archive..."), KMessageWidget::Information);
2127 const KArchiveDirectory *archiveDir = archive->directory();
2128 if (!archiveDir->copyTo(dir.absolutePath())) {
2129 qDebug()<<"=== Error extracting archive!!";
2130 } else {
2131 QFile::remove(archiveFile);
2132 emit parseDictionaries();
2133 doShowSpeechMessage(i18n("New dictionary installed."), KMessageWidget::Positive);
2134 }
2135 } else {
2136 // Test if it is a folder
2137 QDir dir(archiveFile);
2138 if (dir.exists()) {
2139
2140 }
2141 qDebug()<<"=== CANNOT OPEN ARCHIVE!!";
2142 }
2143 }
2144
slotParseVoskDictionaries()2145 void KdenliveSettingsDialog::slotParseVoskDictionaries()
2146 {
2147 m_speechListWidget->clear();
2148 QString modelDirectory = KdenliveSettings::vosk_folder_path();
2149 QDir dir;
2150 if (modelDirectory.isEmpty()) {
2151 modelDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
2152 dir = QDir(modelDirectory);
2153 if (!dir.cd(QStringLiteral("speechmodels"))) {
2154 qDebug()<<"=== /// CANNOT ACCESS SPEECH DICTIONARIES FOLDER";
2155 emit pCore->voskModelUpdate({});
2156 return;
2157 }
2158 } else {
2159 dir = QDir(modelDirectory);
2160 }
2161 QStringList dicts = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
2162 QStringList final;
2163 for (auto &d : dicts) {
2164 QDir sub(dir.absoluteFilePath(d));
2165 if (sub.exists(QStringLiteral("mfcc.conf")) || (sub.exists(QStringLiteral("conf/mfcc.conf")))) {
2166 final << d;
2167 }
2168 }
2169 m_speechListWidget->addItems(final);
2170 if (!KdenliveSettings::vosk_folder_path().isEmpty()) {
2171 m_configSpeech.custom_vosk_folder->setChecked(true);
2172 m_configSpeech.vosk_folder->setUrl(QUrl::fromLocalFile(KdenliveSettings::vosk_folder_path()));
2173 }
2174 if (!final.isEmpty() && KdenliveSettings::vosk_found() && KdenliveSettings::vosk_srt_found()) {
2175 m_configSpeech.speech_info->animatedHide();
2176 } else if (final.isEmpty()) {
2177 doShowSpeechMessage(i18n("Please add a speech model."), KMessageWidget::Information);
2178 }
2179 emit pCore->voskModelUpdate(final);
2180 }
2181
2182