diff --git a/src/assets/view/widgets/keywordparamwidget.cpp b/src/assets/view/widgets/keywordparamwidget.cpp
index 38dc99783..6a8e4f620 100644
--- a/src/assets/view/widgets/keywordparamwidget.cpp
+++ b/src/assets/view/widgets/keywordparamwidget.cpp
@@ -1,83 +1,79 @@
/***************************************************************************
* Copyright (C) 2016 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
***************************************************************************/
#include "keywordparamwidget.hpp"
#include "assets/model/assetparametermodel.hpp"
KeywordParamWidget::KeywordParamWidget(std::shared_ptr model, QModelIndex index, QWidget *parent)
: AbstractParamWidget(std::move(model), index, parent)
{
setupUi(this);
// setup the comment
QString name = m_model->data(m_index, AssetParameterModel::NameRole).toString();
QString comment = m_model->data(m_index, AssetParameterModel::CommentRole).toString();
setToolTip(comment);
// setup the name
label->setText(m_model->data(m_index, Qt::DisplayRole).toString());
QStringList kwrdValues = m_model->data(m_index, AssetParameterModel::ListValuesRole).toStringList();
QStringList kwrdNames = m_model->data(m_index, AssetParameterModel::ListNamesRole).toStringList();
comboboxwidget->addItems(kwrdNames);
int i = 0;
for (const QString &keywordVal : kwrdValues) {
if (i >= comboboxwidget->count()) {
break;
}
comboboxwidget->setItemData(i, keywordVal);
i++;
}
comboboxwidget->insertItem(0, i18n(""));
comboboxwidget->setCurrentIndex(0);
// set check state
slotRefresh();
setMinimumHeight(comboboxwidget->sizeHint().height());
// emit the signal of the base class when appropriate
connect(lineeditwidget, &QLineEdit::editingFinished, [this]() {
emit valueChanged(m_index, lineeditwidget->text(), true);
});
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(comboboxwidget, static_cast(&QComboBox::currentIndexChanged),
-#else
- connect(comboboxwidget, static_cast(&QComboBox::currentIndexChanged) ,
-#endif
[this](int ix) {
if (ix > 0) {
QString comboval = comboboxwidget->itemData(ix).toString();
this->lineeditwidget->insert(comboval);
emit valueChanged(m_index, lineeditwidget->text(), true);
comboboxwidget->setCurrentIndex(0);
}
});
}
void KeywordParamWidget::slotShowComment(bool show)
{
Q_UNUSED(show);
}
void KeywordParamWidget::slotRefresh()
{
lineeditwidget->setText(m_model->data(m_index, AssetParameterModel::ValueRole).toString());
}
diff --git a/src/assets/view/widgets/listparamwidget.cpp b/src/assets/view/widgets/listparamwidget.cpp
index a76784e76..fa8144f82 100644
--- a/src/assets/view/widgets/listparamwidget.cpp
+++ b/src/assets/view/widgets/listparamwidget.cpp
@@ -1,153 +1,149 @@
/***************************************************************************
* Copyright (C) 2016 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
***************************************************************************/
#include "listparamwidget.h"
#include "assets/model/assetparametermodel.hpp"
#include "core.h"
#include "mainwindow.h"
ListParamWidget::ListParamWidget(std::shared_ptr model, QModelIndex index, QWidget *parent)
: AbstractParamWidget(std::move(model), index, parent)
{
setupUi(this);
// Get data from model
QString name = m_model->data(m_index, AssetParameterModel::NameRole).toString();
QString comment = m_model->data(m_index, AssetParameterModel::CommentRole).toString();
// setup the comment
setToolTip(comment);
m_labelComment->setText(comment);
m_widgetComment->setHidden(true);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_list->setIconSize(QSize(50, 30));
setMinimumHeight(m_list->sizeHint().height());
// setup the name
m_labelName->setText(m_model->data(m_index, Qt::DisplayRole).toString());
slotRefresh();
// emit the signal of the base class when appropriate
// The connection is ugly because the signal "currentIndexChanged" is overloaded in QComboBox
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(this->m_list, static_cast(&QComboBox::currentIndexChanged),
-#else
- connect(this->m_list, static_cast(&QComboBox::currentIndexChanged) ,
-#endif
[this](int) {
emit valueChanged(m_index, m_list->itemData(m_list->currentIndex()).toString(), true);
});
}
void ListParamWidget::setCurrentIndex(int index)
{
m_list->setCurrentIndex(index);
}
void ListParamWidget::setCurrentText(const QString &text)
{
m_list->setCurrentText(text);
}
void ListParamWidget::addItem(const QString &text, const QVariant &value)
{
m_list->addItem(text, value);
}
void ListParamWidget::setItemIcon(int index, const QIcon &icon)
{
m_list->setItemIcon(index, icon);
}
void ListParamWidget::setIconSize(const QSize &size)
{
m_list->setIconSize(size);
}
void ListParamWidget::slotShowComment(bool show)
{
if (!m_labelComment->text().isEmpty()) {
m_widgetComment->setVisible(show);
}
}
QString ListParamWidget::getValue()
{
return m_list->currentData().toString();
}
void ListParamWidget::slotRefresh()
{
const QSignalBlocker bk(m_list);
m_list->clear();
QStringList names = m_model->data(m_index, AssetParameterModel::ListNamesRole).toStringList();
QStringList values = m_model->data(m_index, AssetParameterModel::ListValuesRole).toStringList();
QString value = m_model->data(m_index, AssetParameterModel::ValueRole).toString();
if (values.first() == QLatin1String("%lumaPaths")) {
// Special case: Luma files
// Create thumbnails
if (pCore->getCurrentFrameSize().width() > 1000) {
// HD project
values = MainWindow::m_lumaFiles.value(QStringLiteral("16_9"));
} else if (pCore->getCurrentFrameSize().height() > 1000) {
values = MainWindow::m_lumaFiles.value(QStringLiteral("9_16"));
} else if (pCore->getCurrentFrameSize().height() == pCore->getCurrentFrameSize().width()) {
values = MainWindow::m_lumaFiles.value(QStringLiteral("square"));
} else if (pCore->getCurrentFrameSize().height() == 480) {
values = MainWindow::m_lumaFiles.value(QStringLiteral("NTSC"));
} else {
values = MainWindow::m_lumaFiles.value(QStringLiteral("PAL"));
}
m_list->addItem(i18n("None (Dissolve)"));
for (int j = 0; j < values.count(); ++j) {
const QString &entry = values.at(j);
m_list->addItem(values.at(j).section(QLatin1Char('/'), -1), entry);
if (!entry.isEmpty() && (entry.endsWith(QLatin1String(".png")) || entry.endsWith(QLatin1String(".pgm")))) {
if (MainWindow::m_lumacache.contains(entry)) {
m_list->setItemIcon(j + 1, QPixmap::fromImage(MainWindow::m_lumacache.value(entry)));
}
}
}
if (!value.isEmpty() && values.contains(value)) {
m_list->setCurrentIndex(values.indexOf(value) + 1);
}
} else {
if (names.count() != values.count()) {
names = values;
}
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
for (int i = 0; i < names.count(); i++) {
QString val = values.at(i);
bool ok;
double num = val.toDouble(&ok);
if (ok) {
val = locale.toString(num);
}
m_list->addItem(names.at(i), val);
}
if (!value.isEmpty()) {
int ix = m_list->findData(value);
if (ix > -1) {
m_list->setCurrentIndex(ix);
}
}
}
}
diff --git a/src/dialogs/encodingprofilesdialog.cpp b/src/dialogs/encodingprofilesdialog.cpp
index 89870f7b4..ec6bd55fc 100644
--- a/src/dialogs/encodingprofilesdialog.cpp
+++ b/src/dialogs/encodingprofilesdialog.cpp
@@ -1,194 +1,190 @@
/***************************************************************************
* Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "encodingprofilesdialog.h"
#include "kdenlivesettings.h"
#include "klocalizedstring.h"
#include
#include
#include
#include
EncodingProfilesDialog::EncodingProfilesDialog(int profileType, QWidget *parent)
: QDialog(parent)
, m_configGroup(nullptr)
{
setupUi(this);
setWindowTitle(i18n("Manage Encoding Profiles"));
profile_type->addItem(i18n("Proxy clips"), 0);
profile_type->addItem(i18n("Timeline preview"), 1);
profile_type->addItem(i18n("Video4Linux capture"), 2);
profile_type->addItem(i18n("Screen capture"), 3);
profile_type->addItem(i18n("Decklink capture"), 4);
button_add->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
button_edit->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
button_delete->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
button_download->setIcon(QIcon::fromTheme(QStringLiteral("download")));
m_configFile = new KConfig(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
profile_type->setCurrentIndex(profileType);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(profile_type, static_cast(&KComboBox::currentIndexChanged), this, &EncodingProfilesDialog::slotLoadProfiles);
-#else
- connect(profile_type, static_cast(&KComboBox::currentIndexChanged) , this, &EncodingProfilesDialog::slotLoadProfiles);
-#endif
connect(profile_list, &QListWidget::currentRowChanged, this, &EncodingProfilesDialog::slotShowParams);
connect(button_delete, &QAbstractButton::clicked, this, &EncodingProfilesDialog::slotDeleteProfile);
connect(button_add, &QAbstractButton::clicked, this, &EncodingProfilesDialog::slotAddProfile);
connect(button_edit, &QAbstractButton::clicked, this, &EncodingProfilesDialog::slotEditProfile);
profile_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5);
slotLoadProfiles();
}
EncodingProfilesDialog::~EncodingProfilesDialog()
{
delete m_configGroup;
delete m_configFile;
}
void EncodingProfilesDialog::slotLoadProfiles()
{
profile_list->blockSignals(true);
profile_list->clear();
QString group;
switch (profile_type->currentIndex()) {
case 0:
group = QStringLiteral("proxy");
break;
case 2:
group = QStringLiteral("video4linux");
break;
case 3:
group = QStringLiteral("screengrab");
break;
case 4:
group = QStringLiteral("decklink");
break;
case 1:
default:
group = QStringLiteral("timelinepreview");
break;
}
delete m_configGroup;
m_configGroup = new KConfigGroup(m_configFile, group);
QMap values = m_configGroup->entryMap();
QMapIterator i(values);
while (i.hasNext()) {
i.next();
auto *item = new QListWidgetItem(i.key(), profile_list);
item->setData(Qt::UserRole, i.value());
// cout << i.key() << ": " << i.value() << endl;
}
profile_list->blockSignals(false);
profile_list->setCurrentRow(0);
const bool multiProfile(profile_list->count() > 0);
button_delete->setEnabled(multiProfile);
button_edit->setEnabled(multiProfile);
}
void EncodingProfilesDialog::slotShowParams()
{
profile_parameters->clear();
QListWidgetItem *item = profile_list->currentItem();
if (!item) {
return;
}
profile_parameters->setPlainText(item->data(Qt::UserRole).toString().section(QLatin1Char(';'), 0, 0));
}
void EncodingProfilesDialog::slotDeleteProfile()
{
QListWidgetItem *item = profile_list->currentItem();
if (!item) {
return;
}
QString profile = item->text();
m_configGroup->deleteEntry(profile);
slotLoadProfiles();
}
void EncodingProfilesDialog::slotAddProfile()
{
QPointer d = new QDialog(this);
auto *l = new QVBoxLayout;
l->addWidget(new QLabel(i18n("Profile name:")));
auto *pname = new QLineEdit;
l->addWidget(pname);
l->addWidget(new QLabel(i18n("Parameters:")));
auto *pparams = new QPlainTextEdit;
l->addWidget(pparams);
l->addWidget(new QLabel(i18n("File extension:")));
auto *pext = new QLineEdit;
l->addWidget(pext);
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
connect(box, &QDialogButtonBox::accepted, d.data(), &QDialog::accept);
connect(box, &QDialogButtonBox::rejected, d.data(), &QDialog::reject);
l->addWidget(box);
d->setLayout(l);
QListWidgetItem *item = profile_list->currentItem();
if (item) {
QString profilestr = item->data(Qt::UserRole).toString();
pparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
pext->setText(profilestr.section(QLatin1Char(';'), 1, 1));
}
if (d->exec() == QDialog::Accepted) {
m_configGroup->writeEntry(pname->text(), pparams->toPlainText() + QLatin1Char(';') + pext->text());
slotLoadProfiles();
}
delete d;
}
void EncodingProfilesDialog::slotEditProfile()
{
QPointer d = new QDialog(this);
auto *l = new QVBoxLayout;
l->addWidget(new QLabel(i18n("Profile name:")));
auto *pname = new QLineEdit;
l->addWidget(pname);
l->addWidget(new QLabel(i18n("Parameters:")));
auto *pparams = new QPlainTextEdit;
l->addWidget(pparams);
l->addWidget(new QLabel(i18n("File extension:")));
auto *pext = new QLineEdit;
l->addWidget(pext);
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
connect(box, &QDialogButtonBox::accepted, d.data(), &QDialog::accept);
connect(box, &QDialogButtonBox::rejected, d.data(), &QDialog::reject);
l->addWidget(box);
d->setLayout(l);
QListWidgetItem *item = profile_list->currentItem();
if (item) {
pname->setText(item->text());
QString profilestr = item->data(Qt::UserRole).toString();
pparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
pext->setText(profilestr.section(QLatin1Char(';'), 1, 1));
pparams->setFocus();
}
if (d->exec() == QDialog::Accepted) {
m_configGroup->writeEntry(pname->text(), pparams->toPlainText().simplified() + QLatin1Char(';') + pext->text());
slotLoadProfiles();
}
delete d;
}
diff --git a/src/dialogs/kdenlivesettingsdialog.cpp b/src/dialogs/kdenlivesettingsdialog.cpp
index 9dd32d224..77b2daa28 100644
--- a/src/dialogs/kdenlivesettingsdialog.cpp
+++ b/src/dialogs/kdenlivesettingsdialog.cpp
@@ -1,1701 +1,1649 @@
/***************************************************************************
* Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "kdenlivesettingsdialog.h"
#include "clipcreationdialog.h"
#include "core.h"
#include "dialogs/profilesdialog.h"
#include "encodingprofilesdialog.h"
#include "kdenlivesettings.h"
#include "profiles/profilemodel.hpp"
#include "profiles/profilerepository.hpp"
#include "profilesdialog.h"
#include "project/dialogs/profilewidget.h"
#ifdef USE_V4L
#include "capture/v4lcapture.h"
#endif
#include "kdenlive_debug.h"
#include "klocalizedstring.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef USE_JOGSHUTTLE
#include "jogshuttle/jogaction.h"
#include "jogshuttle/jogshuttleconfig.h"
#include
#include
#endif
KdenliveSettingsDialog::KdenliveSettingsDialog(QMap mappable_actions, bool gpuAllowed, QWidget *parent)
: KConfigDialog(parent, QStringLiteral("settings"), KdenliveSettings::self())
, m_modified(false)
, m_shuttleModified(false)
, m_mappable_actions(std::move(mappable_actions))
{
KdenliveSettings::setV4l_format(0);
QWidget *p1 = new QWidget;
QFontInfo ftInfo(font());
m_configMisc.setupUi(p1);
m_page1 = addPage(p1, i18n("Misc"));
m_page1->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
m_configMisc.kcfg_use_exiftool->setEnabled(!QStandardPaths::findExecutable(QStringLiteral("exiftool")).isEmpty());
QWidget *p8 = new QWidget;
m_configProject.setupUi(p8);
m_page8 = addPage(p8, i18n("Project Defaults"));
auto *vbox = new QVBoxLayout;
m_pw = new ProfileWidget(this);
vbox->addWidget(m_pw);
m_configProject.profile_box->setLayout(vbox);
m_configProject.profile_box->setTitle(i18n("Select the default profile (preset)"));
// Select profile
m_pw->loadProfile(KdenliveSettings::default_profile().isEmpty() ? pCore->getCurrentProfile()->path() : KdenliveSettings::default_profile());
connect(m_pw, &ProfileWidget::profileChanged, this, &KdenliveSettingsDialog::slotDialogModified);
m_page8->setIcon(QIcon::fromTheme(QStringLiteral("project-defaults")));
m_configProject.projecturl->setMode(KFile::Directory);
m_configProject.projecturl->setUrl(QUrl::fromLocalFile(KdenliveSettings::defaultprojectfolder()));
connect(m_configProject.kcfg_videotracks, static_cast(&QSpinBox::valueChanged), [this]() {
if (m_configProject.kcfg_videotracks->value() + m_configProject.kcfg_audiotracks->value() <= 0) {
m_configProject.kcfg_videotracks->setValue(1);
}
});
connect(m_configProject.kcfg_audiotracks, static_cast(&QSpinBox::valueChanged), [this] () {
if (m_configProject.kcfg_videotracks->value() + m_configProject.kcfg_audiotracks->value() <= 0) {
m_configProject.kcfg_audiotracks->setValue(1);
}
});
QWidget *p9 = new QWidget;
m_configProxy.setupUi(p9);
KPageWidgetItem *page9 = addPage(p9, i18n("Proxy Clips"));
page9->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out")));
connect(m_configProxy.kcfg_generateproxy, &QAbstractButton::toggled, m_configProxy.kcfg_proxyminsize, &QWidget::setEnabled);
m_configProxy.kcfg_proxyminsize->setEnabled(KdenliveSettings::generateproxy());
connect(m_configProxy.kcfg_generateimageproxy, &QAbstractButton::toggled, m_configProxy.kcfg_proxyimageminsize, &QWidget::setEnabled);
m_configProxy.kcfg_proxyimageminsize->setEnabled(KdenliveSettings::generateimageproxy());
loadExternalProxyProfiles();
QWidget *p3 = new QWidget;
m_configTimeline.setupUi(p3);
m_page3 = addPage(p3, i18n("Timeline"));
m_page3->setIcon(QIcon::fromTheme(QStringLiteral("video-display")));
m_configTimeline.kcfg_trackheight->setMinimum(ftInfo.pixelSize() * 1.5);
QWidget *p2 = new QWidget;
m_configEnv.setupUi(p2);
m_configEnv.mltpathurl->setMode(KFile::Directory);
m_configEnv.mltpathurl->lineEdit()->setObjectName(QStringLiteral("kcfg_mltpath"));
m_configEnv.rendererpathurl->lineEdit()->setObjectName(QStringLiteral("kcfg_rendererpath"));
m_configEnv.ffmpegurl->lineEdit()->setObjectName(QStringLiteral("kcfg_ffmpegpath"));
m_configEnv.ffplayurl->lineEdit()->setObjectName(QStringLiteral("kcfg_ffplaypath"));
m_configEnv.ffprobeurl->lineEdit()->setObjectName(QStringLiteral("kcfg_ffprobepath"));
int maxThreads = QThread::idealThreadCount();
m_configEnv.kcfg_mltthreads->setMaximum(maxThreads > 2 ? maxThreads : 8);
m_configEnv.tmppathurl->setMode(KFile::Directory);
m_configEnv.tmppathurl->lineEdit()->setObjectName(QStringLiteral("kcfg_currenttmpfolder"));
m_configEnv.capturefolderurl->setMode(KFile::Directory);
m_configEnv.capturefolderurl->lineEdit()->setObjectName(QStringLiteral("kcfg_capturefolder"));
m_configEnv.capturefolderurl->setEnabled(!KdenliveSettings::capturetoprojectfolder());
connect(m_configEnv.kcfg_capturetoprojectfolder, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEnableCaptureFolder);
// Library folder
m_configEnv.libraryfolderurl->setMode(KFile::Directory);
m_configEnv.libraryfolderurl->lineEdit()->setObjectName(QStringLiteral("kcfg_libraryfolder"));
m_configEnv.libraryfolderurl->setEnabled(!KdenliveSettings::librarytodefaultfolder());
m_configEnv.kcfg_librarytodefaultfolder->setToolTip(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/library"));
connect(m_configEnv.kcfg_librarytodefaultfolder, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEnableLibraryFolder);
// Mime types
QStringList mimes = ClipCreationDialog::getExtensions();
std::sort(mimes.begin(), mimes.end());
m_configEnv.supportedmimes->setPlainText(mimes.join(QLatin1Char(' ')));
m_page2 = addPage(p2, i18n("Environment"));
m_page2->setIcon(QIcon::fromTheme(QStringLiteral("application-x-executable-script")));
QWidget *p4 = new QWidget;
m_configCapture.setupUi(p4);
// Remove ffmpeg tab, unused
m_configCapture.tabWidget->removeTab(0);
m_configCapture.label->setVisible(false);
m_configCapture.kcfg_defaultcapture->setVisible(false);
//m_configCapture.tabWidget->removeTab(2);
#ifdef USE_V4L
// Video 4 Linux device detection
for (int i = 0; i < 10; ++i) {
QString path = QStringLiteral("/dev/video") + QString::number(i);
if (QFile::exists(path)) {
QStringList deviceInfo = V4lCaptureHandler::getDeviceName(path);
if (!deviceInfo.isEmpty()) {
m_configCapture.kcfg_detectedv4ldevices->addItem(deviceInfo.at(0), path);
m_configCapture.kcfg_detectedv4ldevices->setItemData(m_configCapture.kcfg_detectedv4ldevices->count() - 1, deviceInfo.at(1), Qt::UserRole + 1);
}
}
}
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configCapture.kcfg_detectedv4ldevices, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configCapture.kcfg_detectedv4ldevices, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdatev4lDevice);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configCapture.kcfg_v4l_format, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configCapture.kcfg_v4l_format, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdatev4lCaptureProfile);
connect(m_configCapture.config_v4l, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditVideo4LinuxProfile);
slotUpdatev4lDevice();
#endif
m_page4 = addPage(p4, i18n("Capture"));
m_page4->setIcon(QIcon::fromTheme(QStringLiteral("media-record")));
m_configCapture.tabWidget->setCurrentIndex(KdenliveSettings::defaultcapture());
#ifdef Q_WS_MAC
m_configCapture.tabWidget->setEnabled(false);
m_configCapture.kcfg_defaultcapture->setEnabled(false);
m_configCapture.label->setText(i18n("Capture is not yet available on Mac OS X."));
#endif
QWidget *p5 = new QWidget;
m_configShuttle.setupUi(p5);
#ifdef USE_JOGSHUTTLE
m_configShuttle.toolBtnReload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
connect(m_configShuttle.kcfg_enableshuttle, &QCheckBox::stateChanged, this, &KdenliveSettingsDialog::slotCheckShuttle);
connect(m_configShuttle.shuttledevicelist, SIGNAL(activated(int)), this, SLOT(slotUpdateShuttleDevice(int)));
connect(m_configShuttle.toolBtnReload, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotReloadShuttleDevices);
slotCheckShuttle(static_cast(KdenliveSettings::enableshuttle()));
m_configShuttle.shuttledisabled->hide();
// Store the button pointers into an array for easier handling them in the other functions.
// TODO: impl enumerator or live with cut and paste :-)))
setupJogshuttleBtns(KdenliveSettings::shuttledevice());
#if 0
m_shuttle_buttons.push_back(m_configShuttle.shuttle1);
m_shuttle_buttons.push_back(m_configShuttle.shuttle2);
m_shuttle_buttons.push_back(m_configShuttle.shuttle3);
m_shuttle_buttons.push_back(m_configShuttle.shuttle4);
m_shuttle_buttons.push_back(m_configShuttle.shuttle5);
m_shuttle_buttons.push_back(m_configShuttle.shuttle6);
m_shuttle_buttons.push_back(m_configShuttle.shuttle7);
m_shuttle_buttons.push_back(m_configShuttle.shuttle8);
m_shuttle_buttons.push_back(m_configShuttle.shuttle9);
m_shuttle_buttons.push_back(m_configShuttle.shuttle10);
m_shuttle_buttons.push_back(m_configShuttle.shuttle11);
m_shuttle_buttons.push_back(m_configShuttle.shuttle12);
m_shuttle_buttons.push_back(m_configShuttle.shuttle13);
m_shuttle_buttons.push_back(m_configShuttle.shuttle14);
m_shuttle_buttons.push_back(m_configShuttle.shuttle15);
#endif
#else /* ! USE_JOGSHUTTLE */
m_configShuttle.kcfg_enableshuttle->hide();
m_configShuttle.kcfg_enableshuttle->setDisabled(true);
#endif /* USE_JOGSHUTTLE */
m_page5 = addPage(p5, i18n("JogShuttle"));
m_page5->setIcon(QIcon::fromTheme(QStringLiteral("jog-dial")));
QWidget *p6 = new QWidget;
m_configSdl.setupUi(p6);
m_configSdl.reload_blackmagic->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
connect(m_configSdl.reload_blackmagic, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotReloadBlackMagic);
// m_configSdl.kcfg_openglmonitors->setHidden(true);
m_page6 = addPage(p6, i18n("Playback"));
m_page6->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
QWidget *p7 = new QWidget;
m_configTranscode.setupUi(p7);
m_page7 = addPage(p7, i18n("Transcode"));
m_page7->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
connect(m_configTranscode.button_add, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotAddTranscode);
connect(m_configTranscode.button_delete, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotDeleteTranscode);
connect(m_configTranscode.profiles_list, &QListWidget::itemChanged, this, &KdenliveSettingsDialog::slotDialogModified);
connect(m_configTranscode.profiles_list, &QListWidget::currentRowChanged, this, &KdenliveSettingsDialog::slotSetTranscodeProfile);
connect(m_configTranscode.profile_name, &QLineEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate);
connect(m_configTranscode.profile_description, &QLineEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate);
connect(m_configTranscode.profile_extension, &QLineEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate);
connect(m_configTranscode.profile_parameters, &QPlainTextEdit::textChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate);
connect(m_configTranscode.profile_audioonly, &QCheckBox::stateChanged, this, &KdenliveSettingsDialog::slotEnableTranscodeUpdate);
connect(m_configTranscode.button_update, &QAbstractButton::pressed, this, &KdenliveSettingsDialog::slotUpdateTranscodingProfile);
m_configTranscode.profile_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5);
connect(m_configEnv.kp_image, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditImageApplication);
connect(m_configEnv.kp_audio, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditAudioApplication);
loadEncodingProfiles();
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configSdl.kcfg_audio_driver, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configSdl.kcfg_audio_driver, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotCheckAlsaDriver);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configSdl.kcfg_audio_backend, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configSdl.kcfg_audio_backend, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotCheckAudioBackend);
initDevices();
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configCapture.kcfg_grab_capture_type, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configCapture.kcfg_grab_capture_type, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdateGrabRegionStatus);
slotUpdateGrabRegionStatus();
loadTranscodeProfiles();
// decklink profile
QAction *act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
act->setData(4);
connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
m_configCapture.decklink_manageprofile->setDefaultAction(act);
m_configCapture.decklink_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
m_configCapture.decklink_parameters->setVisible(false);
m_configCapture.decklink_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 4);
m_configCapture.decklink_parameters->setPlainText(KdenliveSettings::decklink_parameters());
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configCapture.kcfg_decklink_profile, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configCapture.kcfg_decklink_profile, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdateDecklinkProfile);
connect(m_configCapture.decklink_showprofileinfo, &QAbstractButton::clicked, m_configCapture.decklink_parameters, &QWidget::setVisible);
// ffmpeg profile
m_configCapture.v4l_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
m_configCapture.v4l_parameters->setVisible(false);
m_configCapture.v4l_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 4);
m_configCapture.v4l_parameters->setPlainText(KdenliveSettings::v4l_parameters());
act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
act->setData(2);
connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
m_configCapture.v4l_manageprofile->setDefaultAction(act);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configCapture.kcfg_v4l_profile, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configCapture.kcfg_v4l_profile, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdateV4lProfile);
connect(m_configCapture.v4l_showprofileinfo, &QAbstractButton::clicked, m_configCapture.v4l_parameters, &QWidget::setVisible);
// screen grab profile
m_configCapture.grab_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
m_configCapture.grab_parameters->setVisible(false);
m_configCapture.grab_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 4);
m_configCapture.grab_parameters->setPlainText(KdenliveSettings::grab_parameters());
act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
act->setData(3);
connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
m_configCapture.grab_manageprofile->setDefaultAction(act);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configCapture.kcfg_grab_profile, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configCapture.kcfg_grab_profile, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdateGrabProfile);
connect(m_configCapture.grab_showprofileinfo, &QAbstractButton::clicked, m_configCapture.grab_parameters, &QWidget::setVisible);
// audio capture channels
m_configCapture.audiocapturechannels->clear();
m_configCapture.audiocapturechannels->addItem(i18n("Mono (1 channel)"), 1);
m_configCapture.audiocapturechannels->addItem(i18n("Stereo (2 channels)"), 2);
int channelsIndex = m_configCapture.audiocapturechannels->findData(KdenliveSettings::audiocapturechannels());
m_configCapture.audiocapturechannels->setCurrentIndex(qMax(channelsIndex, 0));
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configCapture.audiocapturechannels, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configCapture.audiocapturechannels, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdateAudioCaptureChannels);
// audio capture sample rate
m_configCapture.audiocapturesamplerate->clear();
m_configCapture.audiocapturesamplerate->addItem(i18n("44100 Hz"), 44100);
m_configCapture.audiocapturesamplerate->addItem(i18n("48000 Hz"), 48000);
int sampleRateIndex = m_configCapture.audiocapturesamplerate->findData(KdenliveSettings::audiocapturesamplerate());
m_configCapture.audiocapturesamplerate->setCurrentIndex(qMax(sampleRateIndex, 0));
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configCapture.audiocapturesamplerate, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configCapture.audiocapturesamplerate, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdateAudioCaptureSampleRate);
m_configCapture.labelNoAudioDevices->setVisible(false);
// Timeline preview
act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
act->setData(1);
connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
m_configProject.preview_manageprofile->setDefaultAction(act);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configProject.kcfg_preview_profile, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configProject.kcfg_preview_profile, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdatePreviewProfile);
connect(m_configProject.preview_showprofileinfo, &QAbstractButton::clicked, m_configProject.previewparams, &QWidget::setVisible);
m_configProject.previewparams->setVisible(false);
m_configProject.previewparams->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 3);
m_configProject.previewparams->setPlainText(KdenliveSettings::previewparams());
m_configProject.preview_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
m_configProject.preview_showprofileinfo->setToolTip(i18n("Show default timeline preview parameters"));
m_configProject.preview_manageprofile->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
m_configProject.preview_manageprofile->setToolTip(i18n("Manage timeline preview profiles"));
m_configProject.kcfg_preview_profile->setToolTip(i18n("Select default timeline preview profile"));
// proxy profile stuff
m_configProxy.proxy_showprofileinfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
m_configProxy.proxy_showprofileinfo->setToolTip(i18n("Show default profile parameters"));
m_configProxy.proxy_manageprofile->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
m_configProxy.proxy_manageprofile->setToolTip(i18n("Manage proxy profiles"));
m_configProxy.kcfg_proxy_profile->setToolTip(i18n("Select default proxy profile"));
m_configProxy.proxyparams->setVisible(false);
m_configProxy.proxyparams->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 3);
m_configProxy.proxyparams->setPlainText(KdenliveSettings::proxyparams());
act = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure profiles"), this);
act->setData(0);
connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile);
m_configProxy.proxy_manageprofile->setDefaultAction(act);
connect(m_configProxy.proxy_showprofileinfo, &QAbstractButton::clicked, m_configProxy.proxyparams, &QWidget::setVisible);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configProxy.kcfg_proxy_profile, static_cast(&QComboBox::currentIndexChanged), this,
-#else
- connect(m_configProxy.kcfg_proxy_profile, static_cast(&QComboBox::currentIndexChanged) , this,
-#endif
&KdenliveSettingsDialog::slotUpdateProxyProfile);
slotUpdateProxyProfile(-1);
slotUpdateV4lProfile(-1);
slotUpdateGrabProfile(-1);
slotUpdateDecklinkProfile(-1);
// enable GPU accel only if Movit is found
m_configSdl.kcfg_gpu_accel->setEnabled(gpuAllowed);
m_configSdl.kcfg_gpu_accel->setToolTip(i18n("GPU processing needs MLT compiled with Movit and Rtaudio modules"));
getBlackMagicDeviceList(m_configCapture.kcfg_decklink_capturedevice);
if (!getBlackMagicOutputDeviceList(m_configSdl.kcfg_blackmagic_output_device)) {
// No blackmagic card found
m_configSdl.kcfg_external_display->setEnabled(false);
}
initAudioRecDevice();
// Config dialog size
KSharedConfigPtr config = KSharedConfig::openConfig();
KConfigGroup settingsGroup(config, "settings");
QSize optimalSize;
if (!settingsGroup.exists() || !settingsGroup.hasKey("dialogSize")) {
const QSize screenSize = (QGuiApplication::primaryScreen()->availableSize() * 0.9);
const QSize targetSize = QSize(1024, 700);
optimalSize = targetSize.boundedTo(screenSize);
} else {
optimalSize = settingsGroup.readEntry("dialogSize", QVariant(size())).toSize();
}
resize(optimalSize);
}
// static
bool KdenliveSettingsDialog::getBlackMagicDeviceList(QComboBox *devicelist, bool force)
{
if (!force && !KdenliveSettings::decklink_device_found()) {
return false;
}
Mlt::Profile profile;
Mlt::Producer bm(profile, "decklink");
int found_devices = 0;
if (bm.is_valid()) {
bm.set("list_devices", 1);
found_devices = bm.get_int("devices");
} else {
KdenliveSettings::setDecklink_device_found(false);
}
if (found_devices <= 0) {
devicelist->setEnabled(false);
return false;
}
KdenliveSettings::setDecklink_device_found(true);
for (int i = 0; i < found_devices; ++i) {
char *tmp = qstrdup(QStringLiteral("device.%1").arg(i).toUtf8().constData());
devicelist->addItem(bm.get(tmp));
delete[] tmp;
}
return true;
}
// static
bool KdenliveSettingsDialog::initAudioRecDevice()
{
QStringList audioDevices = pCore->getAudioCaptureDevices();
//show a hint to the user to know what to check for in case the device list are empty (common issue)
m_configCapture.labelNoAudioDevices->setVisible(audioDevices.empty());
m_configCapture.kcfg_defaultaudiocapture->addItems(audioDevices);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_configCapture.kcfg_defaultaudiocapture, static_cast(&QComboBox::currentIndexChanged), [&]() {
-#else
- connect(m_configCapture.kcfg_defaultaudiocapture, static_cast(&QComboBox::currentIndexChanged) , [&]() {
-#endif
QString currentDevice = m_configCapture.kcfg_defaultaudiocapture->currentText();
KdenliveSettings::setDefaultaudiocapture(currentDevice);
});
QString selectedDevice = KdenliveSettings::defaultaudiocapture();
int selectedIndex = m_configCapture.kcfg_defaultaudiocapture->findText(selectedDevice);
if (!selectedDevice.isEmpty() && selectedIndex > -1) {
m_configCapture.kcfg_defaultaudiocapture->setCurrentIndex(selectedIndex);
}
return true;
}
bool KdenliveSettingsDialog::getBlackMagicOutputDeviceList(QComboBox *devicelist, bool force)
{
if (!force && !KdenliveSettings::decklink_device_found()) {
return false;
}
Mlt::Profile profile;
Mlt::Consumer bm(profile, "decklink");
int found_devices = 0;
if (bm.is_valid()) {
bm.set("list_devices", 1);
found_devices = bm.get_int("devices");
} else {
KdenliveSettings::setDecklink_device_found(false);
}
if (found_devices <= 0) {
devicelist->setEnabled(false);
return false;
}
KdenliveSettings::setDecklink_device_found(true);
for (int i = 0; i < found_devices; ++i) {
char *tmp = qstrdup(QStringLiteral("device.%1").arg(i).toUtf8().constData());
devicelist->addItem(bm.get(tmp));
delete[] tmp;
}
devicelist->addItem(QStringLiteral("test"));
return true;
}
void KdenliveSettingsDialog::setupJogshuttleBtns(const QString &device)
{
QList list;
QList list1;
list << m_configShuttle.shuttle1;
list << m_configShuttle.shuttle2;
list << m_configShuttle.shuttle3;
list << m_configShuttle.shuttle4;
list << m_configShuttle.shuttle5;
list << m_configShuttle.shuttle6;
list << m_configShuttle.shuttle7;
list << m_configShuttle.shuttle8;
list << m_configShuttle.shuttle9;
list << m_configShuttle.shuttle10;
list << m_configShuttle.shuttle11;
list << m_configShuttle.shuttle12;
list << m_configShuttle.shuttle13;
list << m_configShuttle.shuttle14;
list << m_configShuttle.shuttle15;
list1 << m_configShuttle.label_2; // #1
list1 << m_configShuttle.label_4; // #2
list1 << m_configShuttle.label_3; // #3
list1 << m_configShuttle.label_7; // #4
list1 << m_configShuttle.label_5; // #5
list1 << m_configShuttle.label_6; // #6
list1 << m_configShuttle.label_8; // #7
list1 << m_configShuttle.label_9; // #8
list1 << m_configShuttle.label_10; // #9
list1 << m_configShuttle.label_11; // #10
list1 << m_configShuttle.label_12; // #11
list1 << m_configShuttle.label_13; // #12
list1 << m_configShuttle.label_14; // #13
list1 << m_configShuttle.label_15; // #14
list1 << m_configShuttle.label_16; // #15
for (int i = 0; i < list.count(); ++i) {
list[i]->hide();
list1[i]->hide();
}
#ifdef USE_JOGSHUTTLE
if (!m_configShuttle.kcfg_enableshuttle->isChecked()) {
return;
}
int keysCount = JogShuttle::keysCount(device);
for (int i = 0; i < keysCount; ++i) {
m_shuttle_buttons.push_back(list[i]);
list[i]->show();
list1[i]->show();
}
// populate the buttons with the current configuration. The items are sorted
// according to the user-selected language, so they do not appear in random order.
QMap mappable_actions(m_mappable_actions);
QList action_names = mappable_actions.keys();
QList::Iterator iter = action_names.begin();
// qCDebug(KDENLIVE_LOG) << "::::::::::::::::";
while (iter != action_names.end()) {
// qCDebug(KDENLIVE_LOG) << *iter;
++iter;
}
// qCDebug(KDENLIVE_LOG) << "::::::::::::::::";
std::sort(action_names.begin(), action_names.end());
iter = action_names.begin();
while (iter != action_names.end()) {
// qCDebug(KDENLIVE_LOG) << *iter;
++iter;
}
// qCDebug(KDENLIVE_LOG) << "::::::::::::::::";
// Here we need to compute the action_id -> index-in-action_names. We iterate over the
// action_names, as the sorting may depend on the user-language.
QStringList actions_map = JogShuttleConfig::actionMap(KdenliveSettings::shuttlebuttons());
QMap action_pos;
for (const QString &action_id : actions_map) {
// This loop find out at what index is the string that would map to the action_id.
for (int i = 0; i < action_names.size(); ++i) {
if (mappable_actions[action_names.at(i)] == action_id) {
action_pos[action_id] = i;
break;
}
}
}
int i = 0;
for (QComboBox *button : m_shuttle_buttons) {
button->addItems(action_names);
connect(button, SIGNAL(activated(int)), this, SLOT(slotShuttleModified()));
++i;
if (i < actions_map.size()) {
button->setCurrentIndex(action_pos[actions_map[i]]);
}
}
#else
Q_UNUSED(device)
#endif
}
KdenliveSettingsDialog::~KdenliveSettingsDialog() = default;
void KdenliveSettingsDialog::slotUpdateGrabRegionStatus()
{
m_configCapture.region_group->setHidden(m_configCapture.kcfg_grab_capture_type->currentIndex() != 1);
}
void KdenliveSettingsDialog::slotEnableCaptureFolder()
{
m_configEnv.capturefolderurl->setEnabled(!m_configEnv.kcfg_capturetoprojectfolder->isChecked());
}
void KdenliveSettingsDialog::slotEnableLibraryFolder()
{
m_configEnv.libraryfolderurl->setEnabled(!m_configEnv.kcfg_librarytodefaultfolder->isChecked());
}
void KdenliveSettingsDialog::initDevices()
{
// Fill audio drivers
m_configSdl.kcfg_audio_driver->addItem(i18n("Automatic"), QString());
#if defined(Q_OS_WIN)
//TODO: i18n
m_configSdl.kcfg_audio_driver->addItem("DirectSound", "directsound");
m_configSdl.kcfg_audio_driver->addItem("WinMM", "winmm");
m_configSdl.kcfg_audio_driver->addItem("Wasapi", "wasapi");
#elif !defined(Q_WS_MAC)
m_configSdl.kcfg_audio_driver->addItem(i18n("ALSA"), "alsa");
m_configSdl.kcfg_audio_driver->addItem(i18n("PulseAudio"), "pulseaudio");
m_configSdl.kcfg_audio_driver->addItem(i18n("OSS"), "dsp");
//m_configSdl.kcfg_audio_driver->addItem(i18n("OSS with DMA access"), "dma");
m_configSdl.kcfg_audio_driver->addItem(i18n("Esound daemon"), "esd");
m_configSdl.kcfg_audio_driver->addItem(i18n("ARTS daemon"), "artsc");
#endif
if (!KdenliveSettings::audiodrivername().isEmpty())
for (int i = 1; i < m_configSdl.kcfg_audio_driver->count(); ++i) {
if (m_configSdl.kcfg_audio_driver->itemData(i).toString() == KdenliveSettings::audiodrivername()) {
m_configSdl.kcfg_audio_driver->setCurrentIndex(i);
KdenliveSettings::setAudio_driver((uint)i);
}
}
// Fill the list of audio playback / recording devices
m_configSdl.kcfg_audio_device->addItem(i18n("Default"), QString());
m_configCapture.kcfg_v4l_alsadevice->addItem(i18n("Default"), "default");
if (!QStandardPaths::findExecutable(QStringLiteral("aplay")).isEmpty()) {
m_readProcess.setOutputChannelMode(KProcess::OnlyStdoutChannel);
m_readProcess.setProgram(QStringLiteral("aplay"), QStringList() << QStringLiteral("-l"));
connect(&m_readProcess, &KProcess::readyReadStandardOutput, this, &KdenliveSettingsDialog::slotReadAudioDevices);
m_readProcess.execute(5000);
} else {
// If aplay is not installed on the system, parse the /proc/asound/pcm file
QFile file(QStringLiteral("/proc/asound/pcm"));
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream stream(&file);
QString line = stream.readLine();
QString deviceId;
while (!line.isNull()) {
if (line.contains(QStringLiteral("playback"))) {
deviceId = line.section(QLatin1Char(':'), 0, 0);
m_configSdl.kcfg_audio_device->addItem(line.section(QLatin1Char(':'), 1, 1), QStringLiteral("plughw:%1,%2")
.arg(deviceId.section(QLatin1Char('-'), 0, 0).toInt())
.arg(deviceId.section(QLatin1Char('-'), 1, 1).toInt()));
}
if (line.contains(QStringLiteral("capture"))) {
deviceId = line.section(QLatin1Char(':'), 0, 0);
m_configCapture.kcfg_v4l_alsadevice->addItem(
line.section(QLatin1Char(':'), 1, 1).simplified(),
QStringLiteral("hw:%1,%2").arg(deviceId.section(QLatin1Char('-'), 0, 0).toInt()).arg(deviceId.section(QLatin1Char('-'), 1, 1).toInt()));
}
line = stream.readLine();
}
file.close();
} else {
qCDebug(KDENLIVE_LOG) << " / / / /CANNOT READ PCM";
}
}
// Add pulseaudio capture option
m_configCapture.kcfg_v4l_alsadevice->addItem(i18n("PulseAudio"), "pulse");
if (!KdenliveSettings::audiodevicename().isEmpty()) {
// Select correct alsa device
int ix = m_configSdl.kcfg_audio_device->findData(KdenliveSettings::audiodevicename());
m_configSdl.kcfg_audio_device->setCurrentIndex(ix);
KdenliveSettings::setAudio_device(ix);
}
if (!KdenliveSettings::v4l_alsadevicename().isEmpty()) {
// Select correct alsa device
int ix = m_configCapture.kcfg_v4l_alsadevice->findData(KdenliveSettings::v4l_alsadevicename());
m_configCapture.kcfg_v4l_alsadevice->setCurrentIndex(ix);
KdenliveSettings::setV4l_alsadevice(ix);
}
m_configSdl.kcfg_audio_backend->addItem(i18n("SDL"), KdenliveSettings::sdlAudioBackend());
m_configSdl.kcfg_audio_backend->addItem(i18n("RtAudio"), "rtaudio");
if (!KdenliveSettings::audiobackend().isEmpty()) {
int ix = m_configSdl.kcfg_audio_backend->findData(KdenliveSettings::audiobackend());
m_configSdl.kcfg_audio_backend->setCurrentIndex(ix);
KdenliveSettings::setAudio_backend(ix);
}
m_configSdl.group_sdl->setEnabled(KdenliveSettings::audiobackend().startsWith(QLatin1String("sdl")));
loadCurrentV4lProfileInfo();
}
void KdenliveSettingsDialog::slotReadAudioDevices()
{
QString result = QString(m_readProcess.readAllStandardOutput());
// qCDebug(KDENLIVE_LOG) << "// / / / / / READING APLAY: ";
// qCDebug(KDENLIVE_LOG) << result;
const QStringList lines = result.split(QLatin1Char('\n'));
for (const QString &devicestr : lines) {
////qCDebug(KDENLIVE_LOG) << "// READING LINE: " << data;
if (!devicestr.startsWith(QLatin1Char(' ')) && devicestr.count(QLatin1Char(':')) > 1) {
QString card = devicestr.section(QLatin1Char(':'), 0, 0).section(QLatin1Char(' '), -1);
QString device = devicestr.section(QLatin1Char(':'), 1, 1).section(QLatin1Char(' '), -1);
m_configSdl.kcfg_audio_device->addItem(devicestr.section(QLatin1Char(':'), -1).simplified(), QStringLiteral("plughw:%1,%2").arg(card).arg(device));
m_configCapture.kcfg_v4l_alsadevice->addItem(devicestr.section(QLatin1Char(':'), -1).simplified(),
QStringLiteral("hw:%1,%2").arg(card).arg(device));
}
}
}
void KdenliveSettingsDialog::showPage(int page, int option)
{
switch (page) {
case 1:
setCurrentPage(m_page1);
break;
case 2:
setCurrentPage(m_page2);
break;
case 3:
setCurrentPage(m_page3);
break;
case 4:
setCurrentPage(m_page4);
m_configCapture.tabWidget->setCurrentIndex(option);
break;
case 5:
setCurrentPage(m_page5);
break;
case 6:
setCurrentPage(m_page6);
break;
case 7:
setCurrentPage(m_page7);
break;
default:
setCurrentPage(m_page1);
}
}
void KdenliveSettingsDialog::slotEditAudioApplication()
{
KService::Ptr service;
QPointer dlg = new KOpenWithDialog(QList(), i18n("Select default audio editor"), m_configEnv.kcfg_defaultaudioapp->text(), this);
if (dlg->exec() == QDialog::Accepted) {
service = dlg->service();
m_configEnv.kcfg_defaultaudioapp->setText(KIO::DesktopExecParser::executablePath(service->exec()));
}
delete dlg;
}
void KdenliveSettingsDialog::slotEditImageApplication()
{
QPointer dlg = new KOpenWithDialog(QList(), i18n("Select default image editor"), m_configEnv.kcfg_defaultimageapp->text(), this);
if (dlg->exec() == QDialog::Accepted) {
KService::Ptr service = dlg->service();
m_configEnv.kcfg_defaultimageapp->setText(KIO::DesktopExecParser::executablePath(service->exec()));
}
delete dlg;
}
void KdenliveSettingsDialog::slotCheckShuttle(int state)
{
#ifdef USE_JOGSHUTTLE
m_configShuttle.config_group->setEnabled(state != 0);
m_configShuttle.shuttledevicelist->clear();
QStringList devNames = KdenliveSettings::shuttledevicenames();
QStringList devPaths = KdenliveSettings::shuttledevicepaths();
if (devNames.count() != devPaths.count()) {
return;
}
for (int i = 0; i < devNames.count(); ++i) {
m_configShuttle.shuttledevicelist->addItem(devNames.at(i), devPaths.at(i));
}
if (state != 0) {
setupJogshuttleBtns(m_configShuttle.shuttledevicelist->itemData(m_configShuttle.shuttledevicelist->currentIndex()).toString());
}
#else
Q_UNUSED(state)
#endif /* USE_JOGSHUTTLE */
}
void KdenliveSettingsDialog::slotUpdateShuttleDevice(int ix)
{
#ifdef USE_JOGSHUTTLE
QString device = m_configShuttle.shuttledevicelist->itemData(ix).toString();
// KdenliveSettings::setShuttledevice(device);
setupJogshuttleBtns(device);
m_configShuttle.kcfg_shuttledevice->setText(device);
#else
Q_UNUSED(ix)
#endif /* USE_JOGSHUTTLE */
}
void KdenliveSettingsDialog::updateWidgets()
{
// Revert widgets to last saved state (for example when user pressed "Cancel")
// //qCDebug(KDENLIVE_LOG) << "// // // KCONFIG Revert called";
#ifdef USE_JOGSHUTTLE
// revert jog shuttle device
if (m_configShuttle.shuttledevicelist->count() > 0) {
for (int i = 0; i < m_configShuttle.shuttledevicelist->count(); ++i) {
if (m_configShuttle.shuttledevicelist->itemData(i) == KdenliveSettings::shuttledevice()) {
m_configShuttle.shuttledevicelist->setCurrentIndex(i);
break;
}
}
}
// Revert jog shuttle buttons
QList action_names = m_mappable_actions.keys();
std::sort(action_names.begin(), action_names.end());
QStringList actions_map = JogShuttleConfig::actionMap(KdenliveSettings::shuttlebuttons());
QMap action_pos;
for (const QString &action_id : actions_map) {
// This loop find out at what index is the string that would map to the action_id.
for (int i = 0; i < action_names.size(); ++i) {
if (m_mappable_actions[action_names[i]] == action_id) {
action_pos[action_id] = i;
break;
}
}
}
int i = 0;
for (QComboBox *button : m_shuttle_buttons) {
++i;
if (i < actions_map.size()) {
button->setCurrentIndex(action_pos[actions_map[i]]);
}
}
#endif /* USE_JOGSHUTTLE */
}
void KdenliveSettingsDialog::accept()
{
if (m_pw->selectedProfile().isEmpty()) {
KMessageBox::error(this, i18n("Please select a video profile"));
return;
}
KConfigDialog::accept();
}
void KdenliveSettingsDialog::updateSettings()
{
// Save changes to settings (for example when user pressed "Apply" or "Ok")
// //qCDebug(KDENLIVE_LOG) << "// // // KCONFIG UPDATE called";
if (m_pw->selectedProfile().isEmpty()) {
KMessageBox::error(this, i18n("Please select a video profile"));
return;
}
KdenliveSettings::setDefault_profile(m_pw->selectedProfile());
bool resetProfile = false;
bool resetConsumer = false;
bool fullReset = false;
bool updateCapturePath = false;
bool updateLibrary = false;
/*if (m_configShuttle.shuttledevicelist->count() > 0) {
QString device = m_configShuttle.shuttledevicelist->itemData(m_configShuttle.shuttledevicelist->currentIndex()).toString();
if (device != KdenliveSettings::shuttledevice()) KdenliveSettings::setShuttledevice(device);
}*/
// Capture default folder
if (m_configEnv.kcfg_capturetoprojectfolder->isChecked() != KdenliveSettings::capturetoprojectfolder()) {
KdenliveSettings::setCapturetoprojectfolder(m_configEnv.kcfg_capturetoprojectfolder->isChecked());
updateCapturePath = true;
}
if (m_configProject.projecturl->url().toLocalFile() != KdenliveSettings::defaultprojectfolder()) {
KdenliveSettings::setDefaultprojectfolder(m_configProject.projecturl->url().toLocalFile());
}
if (m_configEnv.capturefolderurl->url().toLocalFile() != KdenliveSettings::capturefolder()) {
KdenliveSettings::setCapturefolder(m_configEnv.capturefolderurl->url().toLocalFile());
updateCapturePath = true;
}
// Library default folder
if (m_configEnv.kcfg_librarytodefaultfolder->isChecked() != KdenliveSettings::librarytodefaultfolder()) {
KdenliveSettings::setLibrarytodefaultfolder(m_configEnv.kcfg_librarytodefaultfolder->isChecked());
updateLibrary = true;
}
if (m_configEnv.libraryfolderurl->url().toLocalFile() != KdenliveSettings::libraryfolder()) {
KdenliveSettings::setLibraryfolder(m_configEnv.libraryfolderurl->url().toLocalFile());
if (!KdenliveSettings::librarytodefaultfolder()) {
updateLibrary = true;
}
}
if (m_configCapture.kcfg_v4l_format->currentIndex() != (int)KdenliveSettings::v4l_format()) {
saveCurrentV4lProfile();
KdenliveSettings::setV4l_format(0);
}
// Check if screengrab is fullscreen
if (m_configCapture.kcfg_grab_capture_type->currentIndex() != KdenliveSettings::grab_capture_type()) {
KdenliveSettings::setGrab_capture_type(m_configCapture.kcfg_grab_capture_type->currentIndex());
emit updateFullScreenGrab();
}
// Check encoding profiles
// FFmpeg
QString profilestr = m_configCapture.kcfg_v4l_profile->currentData().toString();
if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::v4l_parameters() ||
profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::v4l_extension())) {
KdenliveSettings::setV4l_parameters(profilestr.section(QLatin1Char(';'), 0, 0));
KdenliveSettings::setV4l_extension(profilestr.section(QLatin1Char(';'), 1, 1));
}
// screengrab
profilestr = m_configCapture.kcfg_grab_profile->currentData().toString();
if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::grab_parameters() ||
profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::grab_extension())) {
KdenliveSettings::setGrab_parameters(profilestr.section(QLatin1Char(';'), 0, 0));
KdenliveSettings::setGrab_extension(profilestr.section(QLatin1Char(';'), 1, 1));
}
// decklink
profilestr = m_configCapture.kcfg_decklink_profile->currentData().toString();
if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::decklink_parameters() ||
profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::decklink_extension())) {
KdenliveSettings::setDecklink_parameters(profilestr.section(QLatin1Char(';'), 0, 0));
KdenliveSettings::setDecklink_extension(profilestr.section(QLatin1Char(';'), 1, 1));
}
// proxies
profilestr = m_configProxy.kcfg_proxy_profile->currentData().toString();
if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::proxyparams() ||
profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::proxyextension())) {
KdenliveSettings::setProxyparams(profilestr.section(QLatin1Char(';'), 0, 0));
KdenliveSettings::setProxyextension(profilestr.section(QLatin1Char(';'), 1, 1));
}
// external proxies
profilestr = m_configProxy.kcfg_external_proxy_profile->currentData().toString();
if (!profilestr.isEmpty() && (profilestr != KdenliveSettings::externalProxyProfile())) {
KdenliveSettings::setExternalProxyProfile(profilestr);
}
// timeline preview
profilestr = m_configProject.kcfg_preview_profile->currentData().toString();
if (!profilestr.isEmpty() && (profilestr.section(QLatin1Char(';'), 0, 0) != KdenliveSettings::previewparams() ||
profilestr.section(QLatin1Char(';'), 1, 1) != KdenliveSettings::previewextension())) {
KdenliveSettings::setPreviewparams(profilestr.section(QLatin1Char(';'), 0, 0));
KdenliveSettings::setPreviewextension(profilestr.section(QLatin1Char(';'), 1, 1));
}
if (updateCapturePath) {
emit updateCaptureFolder();
}
if (updateLibrary) {
emit updateLibraryFolder();
}
QString value = m_configCapture.kcfg_v4l_alsadevice->currentData().toString();
if (value != KdenliveSettings::v4l_alsadevicename()) {
KdenliveSettings::setV4l_alsadevicename(value);
}
if (m_configSdl.kcfg_external_display->isChecked() != KdenliveSettings::external_display()) {
KdenliveSettings::setExternal_display(m_configSdl.kcfg_external_display->isChecked());
resetConsumer = true;
fullReset = true;
} else if (KdenliveSettings::external_display() &&
KdenliveSettings::blackmagic_output_device() != m_configSdl.kcfg_blackmagic_output_device->currentIndex()) {
resetConsumer = true;
fullReset = true;
}
value = m_configSdl.kcfg_audio_driver->currentData().toString();
if (value != KdenliveSettings::audiodrivername()) {
KdenliveSettings::setAudiodrivername(value);
resetConsumer = true;
}
if (value == QLatin1String("alsa")) {
// Audio device setting is only valid for alsa driver
value = m_configSdl.kcfg_audio_device->currentData().toString();
if (value != KdenliveSettings::audiodevicename()) {
KdenliveSettings::setAudiodevicename(value);
resetConsumer = true;
}
} else if (!KdenliveSettings::audiodevicename().isEmpty()) {
KdenliveSettings::setAudiodevicename(QString());
resetConsumer = true;
}
value = m_configSdl.kcfg_audio_backend->currentData().toString();
if (value != KdenliveSettings::audiobackend()) {
KdenliveSettings::setAudiobackend(value);
resetConsumer = true;
fullReset = true;
}
if (m_configSdl.kcfg_window_background->color() != KdenliveSettings::window_background()) {
KdenliveSettings::setWindow_background(m_configSdl.kcfg_window_background->color());
emit updateMonitorBg();
resetProfile = true;
}
if (m_configSdl.kcfg_volume->value() != KdenliveSettings::volume()) {
KdenliveSettings::setVolume(m_configSdl.kcfg_volume->value());
resetConsumer = true;
}
if (m_configMisc.kcfg_tabposition->currentIndex() != KdenliveSettings::tabposition()) {
KdenliveSettings::setTabposition(m_configMisc.kcfg_tabposition->currentIndex());
}
if (m_configTimeline.kcfg_displayallchannels->isChecked() != KdenliveSettings::displayallchannels()) {
KdenliveSettings::setDisplayallchannels(m_configTimeline.kcfg_displayallchannels->isChecked());
emit audioThumbFormatChanged();
}
if (m_modified) {
// The transcoding profiles were modified, save.
m_modified = false;
saveTranscodeProfiles();
}
#ifdef USE_JOGSHUTTLE
m_shuttleModified = false;
QStringList actions;
actions << QStringLiteral("monitor_pause"); // the Job rest position action.
for (QComboBox *button : m_shuttle_buttons) {
actions << m_mappable_actions[button->currentText()];
}
QString maps = JogShuttleConfig::actionMap(actions);
// fprintf(stderr, "Shuttle config: %s\n", JogShuttleConfig::actionMap(actions).toLatin1().constData());
if (KdenliveSettings::shuttlebuttons() != maps) {
KdenliveSettings::setShuttlebuttons(maps);
}
#endif
bool restart = false;
if (m_configSdl.kcfg_gpu_accel->isChecked() != KdenliveSettings::gpu_accel()) {
// GPU setting was changed, we need to restart Kdenlive or everything will be corrupted
if (KMessageBox::warningContinueCancel(this, i18n("Kdenlive must be restarted to change this setting")) == KMessageBox::Continue) {
restart = true;
} else {
m_configSdl.kcfg_gpu_accel->setChecked(KdenliveSettings::gpu_accel());
}
}
if (m_configTimeline.kcfg_trackheight->value() != KdenliveSettings::trackheight()) {
KdenliveSettings::setTrackheight(m_configTimeline.kcfg_trackheight->value());
emit resetView();
}
if (m_configTimeline.kcfg_autoscroll->isChecked() != KdenliveSettings::autoscroll()) {
KdenliveSettings::setAutoscroll(m_configTimeline.kcfg_autoscroll->isChecked());
pCore->autoScrollChanged();
}
// Mimes
if (m_configEnv.kcfg_addedExtensions->text() != KdenliveSettings::addedExtensions()) {
// Update list
KdenliveSettings::setAddedExtensions(m_configEnv.kcfg_addedExtensions->text());
QStringList mimes = ClipCreationDialog::getExtensions();
std::sort(mimes.begin(), mimes.end());
m_configEnv.supportedmimes->setPlainText(mimes.join(QLatin1Char(' ')));
}
KConfigDialog::settingsChangedSlot();
// KConfigDialog::updateSettings();
if (resetConsumer) {
emit doResetConsumer(fullReset);
}
if (resetProfile) {
emit doResetProfile();
}
if (restart) {
emit restartKdenlive();
}
emit checkTabPosition();
// remembering Config dialog size
KSharedConfigPtr config = KSharedConfig::openConfig();
KConfigGroup settingsGroup(config, "settings");
settingsGroup.writeEntry("dialogSize", QVariant(size()));
}
void KdenliveSettingsDialog::slotCheckAlsaDriver()
{
QString value = m_configSdl.kcfg_audio_driver->itemData(m_configSdl.kcfg_audio_driver->currentIndex()).toString();
m_configSdl.kcfg_audio_device->setEnabled(value == QLatin1String("alsa"));
}
void KdenliveSettingsDialog::slotCheckAudioBackend()
{
QString value = m_configSdl.kcfg_audio_backend->itemData(m_configSdl.kcfg_audio_backend->currentIndex()).toString();
m_configSdl.group_sdl->setEnabled(value.startsWith(QLatin1String("sdl")));
}
void KdenliveSettingsDialog::loadTranscodeProfiles()
{
KSharedConfigPtr config =
KSharedConfig::openConfig(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("kdenlivetranscodingrc")), KConfig::CascadeConfig);
KConfigGroup transConfig(config, "Transcoding");
// read the entries
m_configTranscode.profiles_list->blockSignals(true);
m_configTranscode.profiles_list->clear();
QMap profiles = transConfig.entryMap();
QMapIterator i(profiles);
while (i.hasNext()) {
i.next();
auto *item = new QListWidgetItem(i.key());
QString profilestr = i.value();
if (profilestr.contains(QLatin1Char(';'))) {
item->setToolTip(profilestr.section(QLatin1Char(';'), 1, 1));
}
item->setData(Qt::UserRole, profilestr);
m_configTranscode.profiles_list->addItem(item);
// item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
}
m_configTranscode.profiles_list->blockSignals(false);
m_configTranscode.profiles_list->setCurrentRow(0);
}
void KdenliveSettingsDialog::saveTranscodeProfiles()
{
QString transcodeFile = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/kdenlivetranscodingrc");
KSharedConfigPtr config = KSharedConfig::openConfig(transcodeFile);
KConfigGroup transConfig(config, "Transcoding");
// read the entries
transConfig.deleteGroup();
int max = m_configTranscode.profiles_list->count();
for (int i = 0; i < max; ++i) {
QListWidgetItem *item = m_configTranscode.profiles_list->item(i);
transConfig.writeEntry(item->text(), item->data(Qt::UserRole).toString());
}
config->sync();
}
void KdenliveSettingsDialog::slotAddTranscode()
{
if (!m_configTranscode.profiles_list->findItems(m_configTranscode.profile_name->text(), Qt::MatchExactly).isEmpty()) {
KMessageBox::sorry(this, i18n("A profile with that name already exists"));
return;
}
QListWidgetItem *item = new QListWidgetItem(m_configTranscode.profile_name->text());
QString profilestr = m_configTranscode.profile_parameters->toPlainText();
profilestr.append(" %1." + m_configTranscode.profile_extension->text());
profilestr.append(';');
if (!m_configTranscode.profile_description->text().isEmpty()) {
profilestr.append(m_configTranscode.profile_description->text());
}
if (m_configTranscode.profile_audioonly->isChecked()) {
profilestr.append(";audio");
}
item->setData(Qt::UserRole, profilestr);
m_configTranscode.profiles_list->addItem(item);
m_configTranscode.profiles_list->setCurrentItem(item);
slotDialogModified();
}
void KdenliveSettingsDialog::slotUpdateTranscodingProfile()
{
QListWidgetItem *item = m_configTranscode.profiles_list->currentItem();
if (!item) {
return;
}
m_configTranscode.button_update->setEnabled(false);
item->setText(m_configTranscode.profile_name->text());
QString profilestr = m_configTranscode.profile_parameters->toPlainText();
profilestr.append(" %1." + m_configTranscode.profile_extension->text());
profilestr.append(';');
if (!m_configTranscode.profile_description->text().isEmpty()) {
profilestr.append(m_configTranscode.profile_description->text());
}
if (m_configTranscode.profile_audioonly->isChecked()) {
profilestr.append(QStringLiteral(";audio"));
}
item->setData(Qt::UserRole, profilestr);
slotDialogModified();
}
void KdenliveSettingsDialog::slotDeleteTranscode()
{
QListWidgetItem *item = m_configTranscode.profiles_list->currentItem();
if (item == nullptr) {
return;
}
delete item;
slotDialogModified();
}
void KdenliveSettingsDialog::slotEnableTranscodeUpdate()
{
if (!m_configTranscode.profile_box->isEnabled()) {
return;
}
bool allow = true;
if (m_configTranscode.profile_name->text().isEmpty() || m_configTranscode.profile_extension->text().isEmpty()) {
allow = false;
}
m_configTranscode.button_update->setEnabled(allow);
}
void KdenliveSettingsDialog::slotSetTranscodeProfile()
{
m_configTranscode.profile_box->setEnabled(false);
m_configTranscode.button_update->setEnabled(false);
m_configTranscode.profile_name->clear();
m_configTranscode.profile_description->clear();
m_configTranscode.profile_extension->clear();
m_configTranscode.profile_parameters->clear();
m_configTranscode.profile_audioonly->setChecked(false);
QListWidgetItem *item = m_configTranscode.profiles_list->currentItem();
if (!item) {
return;
}
m_configTranscode.profile_name->setText(item->text());
QString profilestr = item->data(Qt::UserRole).toString();
if (profilestr.contains(QLatin1Char(';'))) {
m_configTranscode.profile_description->setText(profilestr.section(QLatin1Char(';'), 1, 1));
if (profilestr.section(QLatin1Char(';'), 2, 2) == QLatin1String("audio")) {
m_configTranscode.profile_audioonly->setChecked(true);
}
profilestr = profilestr.section(QLatin1Char(';'), 0, 0).simplified();
}
m_configTranscode.profile_extension->setText(profilestr.section(QLatin1Char('.'), -1));
m_configTranscode.profile_parameters->setPlainText(profilestr.section(QLatin1Char(' '), 0, -2));
m_configTranscode.profile_box->setEnabled(true);
}
void KdenliveSettingsDialog::slotShuttleModified()
{
#ifdef USE_JOGSHUTTLE
QStringList actions;
actions << QStringLiteral("monitor_pause"); // the Job rest position action.
for (QComboBox *button : m_shuttle_buttons) {
actions << m_mappable_actions[button->currentText()];
}
QString maps = JogShuttleConfig::actionMap(actions);
m_shuttleModified = KdenliveSettings::shuttlebuttons() != maps;
#endif
KConfigDialog::updateButtons();
}
void KdenliveSettingsDialog::slotDialogModified()
{
m_modified = true;
KConfigDialog::updateButtons();
}
// virtual
bool KdenliveSettingsDialog::hasChanged()
{
if (m_modified || m_shuttleModified) {
return true;
}
return KConfigDialog::hasChanged();
}
void KdenliveSettingsDialog::slotUpdatev4lDevice()
{
QString device = m_configCapture.kcfg_detectedv4ldevices->itemData(m_configCapture.kcfg_detectedv4ldevices->currentIndex()).toString();
if (!device.isEmpty()) {
m_configCapture.kcfg_video4vdevice->setText(device);
}
QString info = m_configCapture.kcfg_detectedv4ldevices->itemData(m_configCapture.kcfg_detectedv4ldevices->currentIndex(), Qt::UserRole + 1).toString();
m_configCapture.kcfg_v4l_format->blockSignals(true);
m_configCapture.kcfg_v4l_format->clear();
QString vl4ProfilePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/video4linux");
if (QFile::exists(vl4ProfilePath)) {
m_configCapture.kcfg_v4l_format->addItem(i18n("Current settings"));
}
QStringList pixelformats = info.split('>', QString::SkipEmptyParts);
QString itemSize;
QString pixelFormat;
QStringList itemRates;
for (int i = 0; i < pixelformats.count(); ++i) {
QString format = pixelformats.at(i).section(QLatin1Char(':'), 0, 0);
QStringList sizes = pixelformats.at(i).split(':', QString::SkipEmptyParts);
pixelFormat = sizes.takeFirst();
for (int j = 0; j < sizes.count(); ++j) {
itemSize = sizes.at(j).section(QLatin1Char('='), 0, 0);
itemRates = sizes.at(j).section(QLatin1Char('='), 1, 1).split(QLatin1Char(','), QString::SkipEmptyParts);
for (int k = 0; k < itemRates.count(); ++k) {
m_configCapture.kcfg_v4l_format->addItem(
QLatin1Char('[') + format + QStringLiteral("] ") + itemSize + QStringLiteral(" (") + itemRates.at(k) + QLatin1Char(')'),
QStringList() << format << itemSize.section('x', 0, 0) << itemSize.section('x', 1, 1) << itemRates.at(k).section(QLatin1Char('/'), 0, 0)
<< itemRates.at(k).section(QLatin1Char('/'), 1, 1));
}
}
}
m_configCapture.kcfg_v4l_format->blockSignals(false);
slotUpdatev4lCaptureProfile();
}
void KdenliveSettingsDialog::slotUpdatev4lCaptureProfile()
{
QStringList info = m_configCapture.kcfg_v4l_format->itemData(m_configCapture.kcfg_v4l_format->currentIndex(), Qt::UserRole).toStringList();
if (info.isEmpty()) {
// No auto info, display the current ones
loadCurrentV4lProfileInfo();
return;
}
m_configCapture.p_size->setText(info.at(1) + QLatin1Char('x') + info.at(2));
m_configCapture.p_fps->setText(info.at(3) + QLatin1Char('/') + info.at(4));
m_configCapture.p_aspect->setText(QStringLiteral("1/1"));
m_configCapture.p_display->setText(info.at(1) + QLatin1Char('/') + info.at(2));
m_configCapture.p_colorspace->setText(ProfileRepository::getColorspaceDescription(601));
m_configCapture.p_progressive->setText(i18n("Progressive"));
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
if (!dir.exists() || !dir.exists(QStringLiteral("video4linux"))) {
saveCurrentV4lProfile();
}
}
void KdenliveSettingsDialog::loadCurrentV4lProfileInfo()
{
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
if (!dir.exists()) {
dir.mkpath(QStringLiteral("."));
}
if (!ProfileRepository::get()->profileExists(dir.absoluteFilePath(QStringLiteral("video4linux")))) {
// No default formats found, build one
std::unique_ptr prof(new ProfileParam(pCore->getCurrentProfile().get()));
prof->m_width = 320;
prof->m_height = 200;
prof->m_frame_rate_num = 15;
prof->m_frame_rate_den = 1;
prof->m_display_aspect_num = 4;
prof->m_display_aspect_den = 3;
prof->m_sample_aspect_num = 1;
prof->m_sample_aspect_den = 1;
prof->m_progressive = true;
prof->m_colorspace = 601;
ProfileRepository::get()->saveProfile(prof.get(), dir.absoluteFilePath(QStringLiteral("video4linux")));
}
auto &prof = ProfileRepository::get()->getProfile(dir.absoluteFilePath(QStringLiteral("video4linux")));
m_configCapture.p_size->setText(QString::number(prof->width()) + QLatin1Char('x') + QString::number(prof->height()));
m_configCapture.p_fps->setText(QString::number(prof->frame_rate_num()) + QLatin1Char('/') + QString::number(prof->frame_rate_den()));
m_configCapture.p_aspect->setText(QString::number(prof->sample_aspect_num()) + QLatin1Char('/') + QString::number(prof->sample_aspect_den()));
m_configCapture.p_display->setText(QString::number(prof->display_aspect_num()) + QLatin1Char('/') + QString::number(prof->display_aspect_den()));
m_configCapture.p_colorspace->setText(ProfileRepository::getColorspaceDescription(prof->colorspace()));
if (prof->progressive()) {
m_configCapture.p_progressive->setText(i18n("Progressive"));
}
}
void KdenliveSettingsDialog::saveCurrentV4lProfile()
{
std::unique_ptr profile(new ProfileParam(pCore->getCurrentProfile().get()));
profile->m_description = QStringLiteral("Video4Linux capture");
profile->m_colorspace = ProfileRepository::getColorspaceFromDescription(m_configCapture.p_colorspace->text());
profile->m_width = m_configCapture.p_size->text().section('x', 0, 0).toInt();
profile->m_height = m_configCapture.p_size->text().section('x', 1, 1).toInt();
profile->m_sample_aspect_num = m_configCapture.p_aspect->text().section(QLatin1Char('/'), 0, 0).toInt();
profile->m_sample_aspect_den = m_configCapture.p_aspect->text().section(QLatin1Char('/'), 1, 1).toInt();
profile->m_display_aspect_num = m_configCapture.p_display->text().section(QLatin1Char('/'), 0, 0).toInt();
profile->m_display_aspect_den = m_configCapture.p_display->text().section(QLatin1Char('/'), 1, 1).toInt();
profile->m_frame_rate_num = m_configCapture.p_fps->text().section(QLatin1Char('/'), 0, 0).toInt();
profile->m_frame_rate_den = m_configCapture.p_fps->text().section(QLatin1Char('/'), 1, 1).toInt();
profile->m_progressive = m_configCapture.p_progressive->text() == i18n("Progressive");
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
if (!dir.exists()) {
dir.mkpath(QStringLiteral("."));
}
ProfileRepository::get()->saveProfile(profile.get(), dir.absoluteFilePath(QStringLiteral("video4linux")));
}
void KdenliveSettingsDialog::slotManageEncodingProfile()
{
auto *act = qobject_cast(sender());
int type = 0;
if (act) {
type = act->data().toInt();
}
QPointer dia = new EncodingProfilesDialog(type);
dia->exec();
delete dia;
loadEncodingProfiles();
}
void KdenliveSettingsDialog::loadExternalProxyProfiles()
{
// load proxy profiles
KConfig conf(QStringLiteral("externalproxies.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
KConfigGroup group(&conf, "proxy");
QMap values = group.entryMap();
QMapIterator k(values);
QString currentItem = KdenliveSettings::externalProxyProfile();
m_configProxy.kcfg_external_proxy_profile->blockSignals(true);
m_configProxy.kcfg_external_proxy_profile->clear();
while (k.hasNext()) {
k.next();
if (!k.key().isEmpty()) {
if (k.value().contains(QLatin1Char(';'))) {
m_configProxy.kcfg_external_proxy_profile->addItem(k.key(), k.value());
}
}
}
if (!currentItem.isEmpty()) {
m_configProxy.kcfg_external_proxy_profile->setCurrentIndex(m_configProxy.kcfg_external_proxy_profile->findText(currentItem));
}
m_configProxy.kcfg_external_proxy_profile->blockSignals(false);
}
void KdenliveSettingsDialog::loadEncodingProfiles()
{
KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
// Load v4l profiles
m_configCapture.kcfg_v4l_profile->blockSignals(true);
QString currentItem = m_configCapture.kcfg_v4l_profile->currentText();
m_configCapture.kcfg_v4l_profile->clear();
KConfigGroup group(&conf, "video4linux");
QMap values = group.entryMap();
QMapIterator i(values);
while (i.hasNext()) {
i.next();
if (!i.key().isEmpty()) {
m_configCapture.kcfg_v4l_profile->addItem(i.key(), i.value());
}
}
m_configCapture.kcfg_v4l_profile->blockSignals(false);
if (!currentItem.isEmpty()) {
m_configCapture.kcfg_v4l_profile->setCurrentIndex(m_configCapture.kcfg_v4l_profile->findText(currentItem));
}
// Load Screen Grab profiles
m_configCapture.kcfg_grab_profile->blockSignals(true);
currentItem = m_configCapture.kcfg_grab_profile->currentText();
m_configCapture.kcfg_grab_profile->clear();
KConfigGroup group2(&conf, "screengrab");
values = group2.entryMap();
QMapIterator j(values);
while (j.hasNext()) {
j.next();
if (!j.key().isEmpty()) {
m_configCapture.kcfg_grab_profile->addItem(j.key(), j.value());
}
}
m_configCapture.kcfg_grab_profile->blockSignals(false);
if (!currentItem.isEmpty()) {
m_configCapture.kcfg_grab_profile->setCurrentIndex(m_configCapture.kcfg_grab_profile->findText(currentItem));
}
// Load Decklink profiles
m_configCapture.kcfg_decklink_profile->blockSignals(true);
currentItem = m_configCapture.kcfg_decklink_profile->currentText();
m_configCapture.kcfg_decklink_profile->clear();
KConfigGroup group3(&conf, "decklink");
values = group3.entryMap();
QMapIterator k(values);
while (k.hasNext()) {
k.next();
if (!k.key().isEmpty()) {
m_configCapture.kcfg_decklink_profile->addItem(k.key(), k.value());
}
}
m_configCapture.kcfg_decklink_profile->blockSignals(false);
if (!currentItem.isEmpty()) {
m_configCapture.kcfg_decklink_profile->setCurrentIndex(m_configCapture.kcfg_decklink_profile->findText(currentItem));
}
// Load Timeline Preview profiles
m_configProject.kcfg_preview_profile->blockSignals(true);
currentItem = m_configProject.kcfg_preview_profile->currentText();
m_configProject.kcfg_preview_profile->clear();
KConfigGroup group5(&conf, "timelinepreview");
values = group5.entryMap();
m_configProject.kcfg_preview_profile->addItem(i18n("Automatic"));
QMapIterator l(values);
while (l.hasNext()) {
l.next();
if (!l.key().isEmpty()) {
m_configProject.kcfg_preview_profile->addItem(l.key(), l.value());
}
}
if (!currentItem.isEmpty()) {
m_configProject.kcfg_preview_profile->setCurrentIndex(m_configProject.kcfg_preview_profile->findText(currentItem));
}
m_configProject.kcfg_preview_profile->blockSignals(false);
QString profilestr = m_configProject.kcfg_preview_profile->itemData(m_configProject.kcfg_preview_profile->currentIndex()).toString();
if (profilestr.isEmpty()) {
m_configProject.previewparams->clear();
} else {
m_configProject.previewparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
}
// Load Proxy profiles
m_configProxy.kcfg_proxy_profile->blockSignals(true);
currentItem = m_configProxy.kcfg_proxy_profile->currentText();
m_configProxy.kcfg_proxy_profile->clear();
KConfigGroup group4(&conf, "proxy");
values = group4.entryMap();
m_configProxy.kcfg_proxy_profile->addItem(i18n("Automatic"));
QMapIterator m(values);
while (m.hasNext()) {
m.next();
if (!m.key().isEmpty()) {
m_configProxy.kcfg_proxy_profile->addItem(m.key(), m.value());
}
}
if (!currentItem.isEmpty()) {
m_configProxy.kcfg_proxy_profile->setCurrentIndex(m_configProxy.kcfg_proxy_profile->findText(currentItem));
}
m_configProxy.kcfg_proxy_profile->blockSignals(false);
profilestr = m_configProxy.kcfg_proxy_profile->itemData(m_configProxy.kcfg_proxy_profile->currentIndex()).toString();
if (profilestr.isEmpty()) {
m_configProxy.proxyparams->clear();
} else {
m_configProxy.proxyparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
}
}
void KdenliveSettingsDialog::slotUpdateDecklinkProfile(int ix)
{
if (ix == -1) {
ix = KdenliveSettings::decklink_profile();
} else {
ix = m_configCapture.kcfg_decklink_profile->currentIndex();
}
QString profilestr = m_configCapture.kcfg_decklink_profile->itemData(ix).toString();
if (profilestr.isEmpty()) {
return;
}
m_configCapture.decklink_parameters->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
//
}
void KdenliveSettingsDialog::slotUpdateV4lProfile(int ix)
{
if (ix == -1) {
ix = KdenliveSettings::v4l_profile();
} else {
ix = m_configCapture.kcfg_v4l_profile->currentIndex();
}
QString profilestr = m_configCapture.kcfg_v4l_profile->itemData(ix).toString();
if (profilestr.isEmpty()) {
return;
}
m_configCapture.v4l_parameters->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
//
}
void KdenliveSettingsDialog::slotUpdateGrabProfile(int ix)
{
if (ix == -1) {
ix = KdenliveSettings::grab_profile();
} else {
ix = m_configCapture.kcfg_grab_profile->currentIndex();
}
QString profilestr = m_configCapture.kcfg_grab_profile->itemData(ix).toString();
if (profilestr.isEmpty()) {
return;
}
m_configCapture.grab_parameters->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
//
}
void KdenliveSettingsDialog::slotUpdateProxyProfile(int ix)
{
if (ix == -1) {
ix = KdenliveSettings::proxy_profile();
} else {
ix = m_configProxy.kcfg_proxy_profile->currentIndex();
}
QString profilestr = m_configProxy.kcfg_proxy_profile->itemData(ix).toString();
if (profilestr.isEmpty()) {
return;
}
m_configProxy.proxyparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
}
void KdenliveSettingsDialog::slotUpdatePreviewProfile(int ix)
{
if (ix == -1) {
ix = KdenliveSettings::preview_profile();
} else {
ix = m_configProject.kcfg_preview_profile->currentIndex();
}
QString profilestr = m_configProject.kcfg_preview_profile->itemData(ix).toString();
if (profilestr.isEmpty()) {
return;
}
m_configProject.previewparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
}
void KdenliveSettingsDialog::slotEditVideo4LinuxProfile()
{
QString vl4ProfilePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/video4linux");
QPointer w = new ProfilesDialog(vl4ProfilePath, true);
if (w->exec() == QDialog::Accepted) {
// save and update profile
loadCurrentV4lProfileInfo();
}
delete w;
}
void KdenliveSettingsDialog::slotReloadBlackMagic()
{
getBlackMagicDeviceList(m_configCapture.kcfg_decklink_capturedevice, true);
if (!getBlackMagicOutputDeviceList(m_configSdl.kcfg_blackmagic_output_device, true)) {
// No blackmagic card found
m_configSdl.kcfg_external_display->setEnabled(false);
}
m_configSdl.kcfg_external_display->setEnabled(KdenliveSettings::decklink_device_found());
}
void KdenliveSettingsDialog::checkProfile()
{
m_pw->loadProfile(KdenliveSettings::default_profile().isEmpty() ? pCore->getCurrentProfile()->path() : KdenliveSettings::default_profile());
}
void KdenliveSettingsDialog::slotReloadShuttleDevices()
{
#ifdef USE_JOGSHUTTLE
QString devDirStr = QStringLiteral("/dev/input/by-id");
QDir devDir(devDirStr);
if (!devDir.exists()) {
devDirStr = QStringLiteral("/dev/input");
}
QStringList devNamesList;
QStringList devPathList;
m_configShuttle.shuttledevicelist->clear();
DeviceMap devMap = JogShuttle::enumerateDevices(devDirStr);
DeviceMapIter iter = devMap.begin();
while (iter != devMap.end()) {
m_configShuttle.shuttledevicelist->addItem(iter.key(), iter.value());
devNamesList << iter.key();
devPathList << iter.value();
++iter;
}
KdenliveSettings::setShuttledevicenames(devNamesList);
KdenliveSettings::setShuttledevicepaths(devPathList);
QTimer::singleShot(200, this, SLOT(slotUpdateShuttleDevice()));
#endif // USE_JOGSHUTTLE
}
void KdenliveSettingsDialog::slotUpdateAudioCaptureChannels(int index)
{
KdenliveSettings::setAudiocapturechannels(m_configCapture.audiocapturechannels->itemData(index).toInt());
}
void KdenliveSettingsDialog::slotUpdateAudioCaptureSampleRate(int index)
{
KdenliveSettings::setAudiocapturesamplerate(m_configCapture.audiocapturesamplerate->itemData(index).toInt());
}
diff --git a/src/dialogs/renderwidget.cpp b/src/dialogs/renderwidget.cpp
index 460668de6..4f88aab87 100644
--- a/src/dialogs/renderwidget.cpp
+++ b/src/dialogs/renderwidget.cpp
@@ -1,3098 +1,3094 @@
/***************************************************************************
* Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "renderwidget.h"
#include "bin/projectitemmodel.h"
#include "bin/bin.h"
#include "core.h"
#include "dialogs/profilesdialog.h"
#include "doc/kdenlivedoc.h"
#include "kdenlivesettings.h"
#include "monitor/monitor.h"
#include "profiles/profilemodel.hpp"
#include "profiles/profilerepository.hpp"
#include "project/projectmanager.h"
#include "timecode.h"
#include "ui_saveprofile_ui.h"
#include "xml/xml.hpp"
#include "klocalizedstring.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "kdenlive_debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef KF5_USE_PURPOSE
#include
#include
#endif
#include
#ifdef Q_OS_MAC
#include
#endif
// Render profiles roles
enum {
GroupRole = Qt::UserRole,
ExtensionRole,
StandardRole,
RenderRole,
ParamsRole,
EditableRole,
ExtraRole,
BitratesRole,
DefaultBitrateRole,
AudioBitratesRole,
DefaultAudioBitrateRole,
SpeedsRole,
FieldRole,
ErrorRole
};
// Render job roles
const int ParametersRole = Qt::UserRole + 1;
const int TimeRole = Qt::UserRole + 2;
const int ProgressRole = Qt::UserRole + 3;
const int ExtraInfoRole = Qt::UserRole + 5;
// Running job status
enum JOBSTATUS { WAITINGJOB = 0, STARTINGJOB, RUNNINGJOB, FINISHEDJOB, FAILEDJOB, ABORTEDJOB };
static QStringList acodecsList;
static QStringList vcodecsList;
static QStringList supportedFormats;
RenderJobItem::RenderJobItem(QTreeWidget *parent, const QStringList &strings, int type)
: QTreeWidgetItem(parent, strings, type)
, m_status(-1)
{
setSizeHint(1, QSize(parent->columnWidth(1), parent->fontMetrics().height() * 3));
setStatus(WAITINGJOB);
}
void RenderJobItem::setStatus(int status)
{
if (m_status == status) {
return;
}
m_status = status;
switch (status) {
case WAITINGJOB:
setIcon(0, QIcon::fromTheme(QStringLiteral("media-playback-pause")));
setData(1, Qt::UserRole, i18n("Waiting..."));
break;
case FINISHEDJOB:
setData(1, Qt::UserRole, i18n("Rendering finished"));
setIcon(0, QIcon::fromTheme(QStringLiteral("dialog-ok")));
setData(1, ProgressRole, 100);
break;
case FAILEDJOB:
setData(1, Qt::UserRole, i18n("Rendering crashed"));
setIcon(0, QIcon::fromTheme(QStringLiteral("dialog-close")));
setData(1, ProgressRole, 100);
break;
case ABORTEDJOB:
setData(1, Qt::UserRole, i18n("Rendering aborted"));
setIcon(0, QIcon::fromTheme(QStringLiteral("dialog-cancel")));
setData(1, ProgressRole, 100);
break;
default:
break;
}
}
int RenderJobItem::status() const
{
return m_status;
}
void RenderJobItem::setMetadata(const QString &data)
{
m_data = data;
}
const QString RenderJobItem::metadata() const
{
return m_data;
}
RenderWidget::RenderWidget(bool enableProxy, QWidget *parent)
: QDialog(parent)
, m_blockProcessing(false)
{
m_view.setupUi(this);
int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
QSize iconSize(size, size);
setWindowTitle(i18n("Rendering"));
m_view.buttonDelete->setIconSize(iconSize);
m_view.buttonEdit->setIconSize(iconSize);
m_view.buttonSave->setIconSize(iconSize);
m_view.buttonFavorite->setIconSize(iconSize);
m_view.buttonDownload->setIconSize(iconSize);
m_view.buttonDelete->setIcon(QIcon::fromTheme(QStringLiteral("trash-empty")));
m_view.buttonDelete->setToolTip(i18n("Delete profile"));
m_view.buttonDelete->setEnabled(false);
m_view.buttonEdit->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
m_view.buttonEdit->setToolTip(i18n("Edit profile"));
m_view.buttonEdit->setEnabled(false);
m_view.buttonSave->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
m_view.buttonSave->setToolTip(i18n("Create new profile"));
m_view.hide_log->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
m_view.buttonFavorite->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
m_view.buttonFavorite->setToolTip(i18n("Copy profile to favorites"));
m_view.buttonDownload->setIcon(QIcon::fromTheme(QStringLiteral("edit-download")));
m_view.buttonDownload->setToolTip(i18n("Download New Render Profiles..."));
m_view.out_file->button()->setToolTip(i18n("Select output destination"));
m_view.advanced_params->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5);
m_view.optionsGroup->setVisible(m_view.options->isChecked());
connect(m_view.options, &QAbstractButton::toggled, m_view.optionsGroup, &QWidget::setVisible);
m_view.videoLabel->setVisible(m_view.options->isChecked());
connect(m_view.options, &QAbstractButton::toggled, m_view.videoLabel, &QWidget::setVisible);
m_view.video->setVisible(m_view.options->isChecked());
connect(m_view.options, &QAbstractButton::toggled, m_view.video, &QWidget::setVisible);
m_view.audioLabel->setVisible(m_view.options->isChecked());
connect(m_view.options, &QAbstractButton::toggled, m_view.audioLabel, &QWidget::setVisible);
m_view.audio->setVisible(m_view.options->isChecked());
connect(m_view.options, &QAbstractButton::toggled, m_view.audio, &QWidget::setVisible);
connect(m_view.quality, &QAbstractSlider::valueChanged, this, &RenderWidget::adjustAVQualities);
connect(m_view.video, static_cast(&QSpinBox::valueChanged), this, &RenderWidget::adjustQuality);
connect(m_view.speed, &QAbstractSlider::valueChanged, this, &RenderWidget::adjustSpeed);
m_view.buttonRender->setEnabled(false);
m_view.buttonGenerateScript->setEnabled(false);
setRescaleEnabled(false);
m_view.guides_box->setVisible(false);
m_view.open_dvd->setVisible(false);
m_view.create_chapter->setVisible(false);
m_view.open_browser->setVisible(false);
m_view.error_box->setVisible(false);
m_view.tc_type->setEnabled(false);
m_view.checkTwoPass->setEnabled(false);
m_view.proxy_render->setHidden(!enableProxy);
connect(m_view.proxy_render, &QCheckBox::toggled, this, &RenderWidget::slotProxyWarn);
KColorScheme scheme(palette().currentColorGroup(), KColorScheme::Window);
QColor bg = scheme.background(KColorScheme::NegativeBackground).color();
m_view.errorBox->setStyleSheet(
QStringLiteral("QGroupBox { background-color: rgb(%1, %2, %3); border-radius: 5px;}; ").arg(bg.red()).arg(bg.green()).arg(bg.blue()));
int height = QFontInfo(font()).pixelSize();
m_view.errorIcon->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-warning")).pixmap(height, height));
m_view.errorBox->setHidden(true);
m_infoMessage = new KMessageWidget;
m_view.info->addWidget(m_infoMessage);
m_infoMessage->setCloseButtonVisible(false);
m_infoMessage->hide();
m_jobInfoMessage = new KMessageWidget;
m_view.jobInfo->addWidget(m_jobInfoMessage);
m_jobInfoMessage->setCloseButtonVisible(false);
m_jobInfoMessage->hide();
m_view.encoder_threads->setMinimum(0);
m_view.encoder_threads->setMaximum(QThread::idealThreadCount());
m_view.encoder_threads->setToolTip(i18n("Encoding threads (0 is automatic)"));
m_view.encoder_threads->setValue(KdenliveSettings::encodethreads());
connect(m_view.encoder_threads, static_cast(&QSpinBox::valueChanged), this, &RenderWidget::slotUpdateEncodeThreads);
m_view.rescale_keep->setChecked(KdenliveSettings::rescalekeepratio());
connect(m_view.rescale_width, static_cast(&QSpinBox::valueChanged), this, &RenderWidget::slotUpdateRescaleWidth);
connect(m_view.rescale_height, static_cast(&QSpinBox::valueChanged), this, &RenderWidget::slotUpdateRescaleHeight);
m_view.rescale_keep->setIcon(QIcon::fromTheme(QStringLiteral("edit-link")));
m_view.rescale_keep->setToolTip(i18n("Preserve aspect ratio"));
connect(m_view.rescale_keep, &QAbstractButton::clicked, this, &RenderWidget::slotSwitchAspectRatio);
connect(m_view.buttonRender, SIGNAL(clicked()), this, SLOT(slotPrepareExport()));
connect(m_view.buttonGenerateScript, &QAbstractButton::clicked, this, &RenderWidget::slotGenerateScript);
m_view.abort_job->setEnabled(false);
m_view.start_script->setEnabled(false);
m_view.delete_script->setEnabled(false);
connect(m_view.export_audio, &QCheckBox::stateChanged, this, &RenderWidget::slotUpdateAudioLabel);
m_view.export_audio->setCheckState(Qt::PartiallyChecked);
checkCodecs();
parseProfiles();
parseScriptFiles();
m_view.running_jobs->setUniformRowHeights(false);
m_view.running_jobs->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_view.running_jobs, &QTreeWidget::customContextMenuRequested, this, &RenderWidget::prepareMenu);
m_view.scripts_list->setUniformRowHeights(false);
connect(m_view.start_script, &QAbstractButton::clicked, this, &RenderWidget::slotStartScript);
connect(m_view.delete_script, &QAbstractButton::clicked, this, &RenderWidget::slotDeleteScript);
connect(m_view.scripts_list, &QTreeWidget::itemSelectionChanged, this, &RenderWidget::slotCheckScript);
connect(m_view.running_jobs, &QTreeWidget::itemSelectionChanged, this, &RenderWidget::slotCheckJob);
connect(m_view.running_jobs, &QTreeWidget::itemDoubleClicked, this, &RenderWidget::slotPlayRendering);
connect(m_view.buttonSave, &QAbstractButton::clicked, this, &RenderWidget::slotSaveProfile);
connect(m_view.buttonEdit, &QAbstractButton::clicked, this, &RenderWidget::slotEditProfile);
connect(m_view.buttonDelete, &QAbstractButton::clicked, this, &RenderWidget::slotDeleteProfile);
connect(m_view.buttonFavorite, &QAbstractButton::clicked, this, &RenderWidget::slotCopyToFavorites);
connect(m_view.buttonDownload, &QAbstractButton::clicked, this, &RenderWidget::slotDownloadNewRenderProfiles);
connect(m_view.abort_job, &QAbstractButton::clicked, this, &RenderWidget::slotAbortCurrentJob);
connect(m_view.start_job, &QAbstractButton::clicked, this, &RenderWidget::slotStartCurrentJob);
connect(m_view.clean_up, &QAbstractButton::clicked, this, &RenderWidget::slotCLeanUpJobs);
connect(m_view.hide_log, &QAbstractButton::clicked, this, &RenderWidget::slotHideLog);
connect(m_view.buttonClose, &QAbstractButton::clicked, this, &QWidget::hide);
connect(m_view.buttonClose2, &QAbstractButton::clicked, this, &QWidget::hide);
connect(m_view.buttonClose3, &QAbstractButton::clicked, this, &QWidget::hide);
connect(m_view.rescale, &QAbstractButton::toggled, this, &RenderWidget::setRescaleEnabled);
connect(m_view.out_file, &KUrlRequester::textChanged, this, static_cast(&RenderWidget::slotUpdateButtons));
connect(m_view.out_file, &KUrlRequester::urlSelected, this, static_cast(&RenderWidget::slotUpdateButtons));
connect(m_view.formats, &QTreeWidget::currentItemChanged, this, &RenderWidget::refreshParams);
connect(m_view.formats, &QTreeWidget::itemDoubleClicked, this, &RenderWidget::slotEditItem);
connect(m_view.render_guide, &QAbstractButton::clicked, this, &RenderWidget::slotUpdateGuideBox);
connect(m_view.render_zone, &QAbstractButton::clicked, this, &RenderWidget::slotUpdateGuideBox);
connect(m_view.render_full, &QAbstractButton::clicked, this, &RenderWidget::slotUpdateGuideBox);
connect(m_view.guide_end, static_cast(&KComboBox::activated), this, &RenderWidget::slotCheckStartGuidePosition);
connect(m_view.guide_start, static_cast(&KComboBox::activated), this, &RenderWidget::slotCheckEndGuidePosition);
connect(m_view.tc_overlay, &QAbstractButton::toggled, m_view.tc_type, &QWidget::setEnabled);
// m_view.splitter->setStretchFactor(1, 5);
// m_view.splitter->setStretchFactor(0, 2);
m_view.out_file->setAcceptMode(QFileDialog::AcceptSave);
m_view.out_file->setMode(KFile::File);
m_view.out_file->setFocusPolicy(Qt::ClickFocus);
m_jobsDelegate = new RenderViewDelegate(this);
m_view.running_jobs->setHeaderLabels(QStringList() << QString() << i18n("File"));
m_view.running_jobs->setItemDelegate(m_jobsDelegate);
QHeaderView *header = m_view.running_jobs->header();
header->setSectionResizeMode(0, QHeaderView::Fixed);
header->resizeSection(0, size + 4);
header->setSectionResizeMode(1, QHeaderView::Interactive);
m_view.scripts_list->setHeaderLabels(QStringList() << QString() << i18n("Stored Playlists"));
m_scriptsDelegate = new RenderViewDelegate(this);
m_view.scripts_list->setItemDelegate(m_scriptsDelegate);
header = m_view.scripts_list->header();
header->setSectionResizeMode(0, QHeaderView::Fixed);
header->resizeSection(0, size + 4);
// Find path for Kdenlive renderer
#ifdef Q_OS_WIN
m_renderer = QCoreApplication::applicationDirPath() + QStringLiteral("/kdenlive_render.exe");
#else
m_renderer = QCoreApplication::applicationDirPath() + QStringLiteral("/kdenlive_render");
#endif
if (!QFile::exists(m_renderer)) {
m_renderer = QStandardPaths::findExecutable(QStringLiteral("kdenlive_render"));
if (m_renderer.isEmpty()) {
m_renderer = QStringLiteral("kdenlive_render");
}
}
QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface();
if ((interface == nullptr) ||
(!interface->isServiceRegistered(QStringLiteral("org.kde.ksmserver")) && !interface->isServiceRegistered(QStringLiteral("org.gnome.SessionManager")))) {
m_view.shutdown->setEnabled(false);
}
#ifdef KF5_USE_PURPOSE
m_shareMenu = new Purpose::Menu();
m_view.shareButton->setMenu(m_shareMenu);
m_view.shareButton->setIcon(QIcon::fromTheme(QStringLiteral("document-share")));
connect(m_shareMenu, &Purpose::Menu::finished, this, &RenderWidget::slotShareActionFinished);
#else
m_view.shareButton->setEnabled(false);
#endif
m_view.parallel_process->setChecked(KdenliveSettings::parallelrender());
connect(m_view.parallel_process, &QCheckBox::stateChanged, [](int state) { KdenliveSettings::setParallelrender(state == Qt::Checked); });
if (KdenliveSettings::gpu_accel()) {
// Disable parallel rendering for movit
m_view.parallel_process->setEnabled(false);
}
m_view.field_order->setEnabled(false);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_view.scanning_list, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { m_view.field_order->setEnabled(index == 2); });
-#else
- connect(m_view.scanning_list, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { m_view.field_order->setEnabled(index == 2); });
-#endif
refreshView();
focusFirstVisibleItem();
adjustSize();
}
void RenderWidget::slotShareActionFinished(const QJsonObject &output, int error, const QString &message)
{
#ifdef KF5_USE_PURPOSE
m_jobInfoMessage->hide();
if (error) {
KMessageBox::error(this, i18n("There was a problem sharing the document: %1", message), i18n("Share"));
} else {
const QString url = output["url"].toString();
if (url.isEmpty()) {
m_jobInfoMessage->setMessageType(KMessageWidget::Positive);
m_jobInfoMessage->setText(i18n("Document shared successfully"));
m_jobInfoMessage->show();
} else {
KMessageBox::information(this, i18n("You can find the shared document at: %1 ", url), i18n("Share"), QString(),
KMessageBox::Notify | KMessageBox::AllowLink);
}
}
#else
Q_UNUSED(output);
Q_UNUSED(error);
Q_UNUSED(message);
#endif
}
QSize RenderWidget::sizeHint() const
{
// Make sure the widget has minimum size on opening
return {200, 200};
}
RenderWidget::~RenderWidget()
{
m_view.running_jobs->blockSignals(true);
m_view.scripts_list->blockSignals(true);
m_view.running_jobs->clear();
m_view.scripts_list->clear();
delete m_jobsDelegate;
delete m_scriptsDelegate;
delete m_infoMessage;
delete m_jobInfoMessage;
}
void RenderWidget::slotEditItem(QTreeWidgetItem *item)
{
if (item->parent() == nullptr) {
// This is a top level item - group - don't edit
return;
}
const QString edit = item->data(0, EditableRole).toString();
if (edit.isEmpty() || !edit.endsWith(QLatin1String("customprofiles.xml"))) {
slotSaveProfile();
} else {
slotEditProfile();
}
}
void RenderWidget::showInfoPanel()
{
if (m_view.advanced_params->isVisible()) {
m_view.advanced_params->setVisible(false);
KdenliveSettings::setShowrenderparams(false);
} else {
m_view.advanced_params->setVisible(true);
KdenliveSettings::setShowrenderparams(true);
}
}
void RenderWidget::updateDocumentPath()
{
if (m_view.out_file->url().isEmpty()) {
return;
}
const QString fileName = m_view.out_file->url().fileName();
m_view.out_file->setUrl(QUrl::fromLocalFile(QDir(pCore->currentDoc()->projectDataFolder()).absoluteFilePath(fileName)));
parseScriptFiles();
}
void RenderWidget::slotUpdateGuideBox()
{
m_view.guides_box->setVisible(m_view.render_guide->isChecked());
}
void RenderWidget::slotCheckStartGuidePosition()
{
if (m_view.guide_start->currentIndex() > m_view.guide_end->currentIndex()) {
m_view.guide_start->setCurrentIndex(m_view.guide_end->currentIndex());
}
}
void RenderWidget::slotCheckEndGuidePosition()
{
if (m_view.guide_end->currentIndex() < m_view.guide_start->currentIndex()) {
m_view.guide_end->setCurrentIndex(m_view.guide_start->currentIndex());
}
}
void RenderWidget::setGuides(std::weak_ptr guidesModel)
{
m_guidesModel = std::move(guidesModel);
reloadGuides();
if (auto ptr = m_guidesModel.lock()) {
connect(ptr.get(), &MarkerListModel::modelChanged, this, &RenderWidget::reloadGuides);
}
}
void RenderWidget::reloadGuides()
{
double projectDuration = GenTime(pCore->projectDuration() - TimelineModel::seekDuration - 2, pCore->getCurrentFps()).ms() / 1000;
QVariant startData = m_view.guide_start->currentData();
QVariant endData = m_view.guide_end->currentData();
m_view.guide_start->clear();
m_view.guide_end->clear();
if (auto ptr = m_guidesModel.lock()) {
QList markers = ptr->getAllMarkers();
double fps = pCore->getCurrentProfile()->fps();
m_view.render_guide->setEnabled(!markers.isEmpty());
if (!markers.isEmpty()) {
m_view.guide_start->addItem(i18n("Beginning"), "0");
m_view.create_chapter->setEnabled(true);
for (auto marker : markers) {
GenTime pos = marker.time();
const QString guidePos = Timecode::getStringTimecode(pos.frames(fps), fps);
m_view.guide_start->addItem(marker.comment() + QLatin1Char('/') + guidePos, pos.seconds());
m_view.guide_end->addItem(marker.comment() + QLatin1Char('/') + guidePos, pos.seconds());
}
m_view.guide_end->addItem(i18n("End"), QString::number(projectDuration));
if (!startData.isNull()) {
int ix = qMax(0, m_view.guide_start->findData(startData));
m_view.guide_start->setCurrentIndex(ix);
}
if (!endData.isNull()) {
int ix = qMax(m_view.guide_start->currentIndex() + 1, m_view.guide_end->findData(endData));
m_view.guide_end->setCurrentIndex(ix);
}
}
} else {
m_view.render_guide->setEnabled(false);
m_view.create_chapter->setEnabled(false);
}
}
/**
* Will be called when the user selects an output file via the file dialog.
* File extension will be added automatically.
*/
void RenderWidget::slotUpdateButtons(const QUrl &url)
{
if (m_view.out_file->url().isEmpty()) {
m_view.buttonGenerateScript->setEnabled(false);
m_view.buttonRender->setEnabled(false);
} else {
updateButtons(); // This also checks whether the selected format is available
}
if (url.isValid()) {
QTreeWidgetItem *item = m_view.formats->currentItem();
if ((item == nullptr) || (item->parent() == nullptr)) { // categories have no parent
m_view.buttonRender->setEnabled(false);
m_view.buttonGenerateScript->setEnabled(false);
return;
}
const QString extension = item->data(0, ExtensionRole).toString();
m_view.out_file->setUrl(filenameWithExtension(url, extension));
}
}
/**
* Will be called when the user changes the output file path in the text line.
* File extension must NOT be added, would make editing impossible!
*/
void RenderWidget::slotUpdateButtons()
{
if (m_view.out_file->url().isEmpty()) {
m_view.buttonRender->setEnabled(false);
m_view.buttonGenerateScript->setEnabled(false);
} else {
updateButtons(); // This also checks whether the selected format is available
}
}
void RenderWidget::slotSaveProfile()
{
Ui::SaveProfile_UI ui;
QPointer d = new QDialog(this);
ui.setupUi(d);
QString customGroup;
QStringList arguments = m_view.advanced_params->toPlainText().split(' ', QString::SkipEmptyParts);
if (!arguments.isEmpty()) {
ui.parameters->setText(arguments.join(QLatin1Char(' ')));
}
ui.profile_name->setFocus();
QTreeWidgetItem *item = m_view.formats->currentItem();
if ((item != nullptr) && (item->parent() != nullptr)) { // not a category
// Duplicate current item settings
customGroup = item->parent()->text(0);
ui.extension->setText(item->data(0, ExtensionRole).toString());
if (ui.parameters->toPlainText().contains(QStringLiteral("%bitrate")) || ui.parameters->toPlainText().contains(QStringLiteral("%quality"))) {
if (ui.parameters->toPlainText().contains(QStringLiteral("%quality"))) {
ui.vbitrates_label->setText(i18n("Qualities"));
ui.default_vbitrate_label->setText(i18n("Default quality"));
} else {
ui.vbitrates_label->setText(i18n("Bitrates"));
ui.default_vbitrate_label->setText(i18n("Default bitrate"));
}
if (item->data(0, BitratesRole).canConvert(QVariant::StringList) && (item->data(0, BitratesRole).toStringList().count() != 0)) {
QStringList bitrates = item->data(0, BitratesRole).toStringList();
ui.vbitrates_list->setText(bitrates.join(QLatin1Char(',')));
if (item->data(0, DefaultBitrateRole).canConvert(QVariant::String)) {
ui.default_vbitrate->setValue(item->data(0, DefaultBitrateRole).toInt());
}
}
} else {
ui.vbitrates->setHidden(true);
}
if (ui.parameters->toPlainText().contains(QStringLiteral("%audiobitrate")) || ui.parameters->toPlainText().contains(QStringLiteral("%audioquality"))) {
if (ui.parameters->toPlainText().contains(QStringLiteral("%audioquality"))) {
ui.abitrates_label->setText(i18n("Qualities"));
ui.default_abitrate_label->setText(i18n("Default quality"));
} else {
ui.abitrates_label->setText(i18n("Bitrates"));
ui.default_abitrate_label->setText(i18n("Default bitrate"));
}
if ((item != nullptr) && item->data(0, AudioBitratesRole).canConvert(QVariant::StringList) &&
(item->data(0, AudioBitratesRole).toStringList().count() != 0)) {
QStringList bitrates = item->data(0, AudioBitratesRole).toStringList();
ui.abitrates_list->setText(bitrates.join(QLatin1Char(',')));
if (item->data(0, DefaultAudioBitrateRole).canConvert(QVariant::String)) {
ui.default_abitrate->setValue(item->data(0, DefaultAudioBitrateRole).toInt());
}
}
} else {
ui.abitrates->setHidden(true);
}
if (item->data(0, SpeedsRole).canConvert(QVariant::StringList) && (item->data(0, SpeedsRole).toStringList().count() != 0)) {
QStringList speeds = item->data(0, SpeedsRole).toStringList();
ui.speeds_list->setText(speeds.join('\n'));
}
}
if (customGroup.isEmpty()) {
customGroup = i18nc("Group Name", "Custom");
}
ui.group_name->setText(customGroup);
if (d->exec() == QDialog::Accepted && !ui.profile_name->text().simplified().isEmpty()) {
QString newProfileName = ui.profile_name->text().simplified();
QString newGroupName = ui.group_name->text().simplified();
if (newGroupName.isEmpty()) {
newGroupName = i18nc("Group Name", "Custom");
}
QDomDocument doc;
QDomElement profileElement = doc.createElement(QStringLiteral("profile"));
profileElement.setAttribute(QStringLiteral("name"), newProfileName);
profileElement.setAttribute(QStringLiteral("category"), newGroupName);
profileElement.setAttribute(QStringLiteral("extension"), ui.extension->text().simplified());
QString args = ui.parameters->toPlainText().simplified();
profileElement.setAttribute(QStringLiteral("args"), args);
if (args.contains(QStringLiteral("%bitrate"))) {
// profile has a variable bitrate
profileElement.setAttribute(QStringLiteral("defaultbitrate"), QString::number(ui.default_vbitrate->value()));
profileElement.setAttribute(QStringLiteral("bitrates"), ui.vbitrates_list->text());
} else if (args.contains(QStringLiteral("%quality"))) {
profileElement.setAttribute(QStringLiteral("defaultquality"), QString::number(ui.default_vbitrate->value()));
profileElement.setAttribute(QStringLiteral("qualities"), ui.vbitrates_list->text());
}
if (args.contains(QStringLiteral("%audiobitrate"))) {
// profile has a variable bitrate
profileElement.setAttribute(QStringLiteral("defaultaudiobitrate"), QString::number(ui.default_abitrate->value()));
profileElement.setAttribute(QStringLiteral("audiobitrates"), ui.abitrates_list->text());
} else if (args.contains(QStringLiteral("%audioquality"))) {
// profile has a variable bitrate
profileElement.setAttribute(QStringLiteral("defaultaudioquality"), QString::number(ui.default_abitrate->value()));
profileElement.setAttribute(QStringLiteral("audioqualities"), ui.abitrates_list->text());
}
QString speeds_list_str = ui.speeds_list->toPlainText();
if (!speeds_list_str.isEmpty()) {
profileElement.setAttribute(QStringLiteral("speeds"), speeds_list_str.replace('\n', ';').simplified());
}
doc.appendChild(profileElement);
saveProfile(doc.documentElement());
parseProfiles();
}
delete d;
}
bool RenderWidget::saveProfile(QDomElement newprofile)
{
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/export/"));
if (!dir.exists()) {
dir.mkpath(QStringLiteral("."));
}
QDomDocument doc;
QFile file(dir.absoluteFilePath(QStringLiteral("customprofiles.xml")));
doc.setContent(&file, false);
file.close();
QDomElement documentElement;
QDomElement profiles = doc.documentElement();
if (profiles.isNull() || profiles.tagName() != QLatin1String("profiles")) {
doc.clear();
profiles = doc.createElement(QStringLiteral("profiles"));
profiles.setAttribute(QStringLiteral("version"), 1);
doc.appendChild(profiles);
}
int version = profiles.attribute(QStringLiteral("version"), nullptr).toInt();
if (version < 1) {
doc.clear();
profiles = doc.createElement(QStringLiteral("profiles"));
profiles.setAttribute(QStringLiteral("version"), 1);
doc.appendChild(profiles);
}
QDomNodeList profilelist = doc.elementsByTagName(QStringLiteral("profile"));
QString newProfileName = newprofile.attribute(QStringLiteral("name"));
// Check existing profiles
QStringList existingProfileNames;
int i = 0;
while (!profilelist.item(i).isNull()) {
documentElement = profilelist.item(i).toElement();
QString profileName = documentElement.attribute(QStringLiteral("name"));
existingProfileNames << profileName;
i++;
}
// Check if a profile with that same name already exists
bool ok;
while (existingProfileNames.contains(newProfileName)) {
QString updatedProfileName = QInputDialog::getText(this, i18n("Profile already exists"),
i18n("This profile name already exists. Change the name if you do not want to overwrite it."),
QLineEdit::Normal, newProfileName, &ok);
if (!ok) {
return false;
}
if (updatedProfileName == newProfileName) {
// remove previous profile
profiles.removeChild(profilelist.item(existingProfileNames.indexOf(newProfileName)));
break;
} else {
newProfileName = updatedProfileName;
newprofile.setAttribute(QStringLiteral("name"), newProfileName);
}
}
profiles.appendChild(newprofile);
// QCString save = doc.toString().utf8();
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
KMessageBox::sorry(this, i18n("Unable to write to file %1", dir.absoluteFilePath("customprofiles.xml")));
return false;
}
QTextStream out(&file);
out << doc.toString();
if (file.error() != QFile::NoError) {
KMessageBox::error(this, i18n("Cannot write to file %1", dir.absoluteFilePath("customprofiles.xml")));
file.close();
return false;
}
file.close();
return true;
}
void RenderWidget::slotCopyToFavorites()
{
QTreeWidgetItem *item = m_view.formats->currentItem();
if ((item == nullptr) || (item->parent() == nullptr)) {
return;
}
QString params = item->data(0, ParamsRole).toString();
QString extension = item->data(0, ExtensionRole).toString();
QString currentProfile = item->text(0);
QDomDocument doc;
QDomElement profileElement = doc.createElement(QStringLiteral("profile"));
profileElement.setAttribute(QStringLiteral("name"), currentProfile);
profileElement.setAttribute(QStringLiteral("category"), i18nc("Category Name", "Custom"));
profileElement.setAttribute(QStringLiteral("destinationid"), QStringLiteral("favorites"));
profileElement.setAttribute(QStringLiteral("extension"), extension);
profileElement.setAttribute(QStringLiteral("args"), params);
if (params.contains(QStringLiteral("%bitrate"))) {
// profile has a variable bitrate
profileElement.setAttribute(QStringLiteral("defaultbitrate"), item->data(0, DefaultBitrateRole).toString());
profileElement.setAttribute(QStringLiteral("bitrates"), item->data(0, BitratesRole).toStringList().join(QLatin1Char(',')));
} else if (params.contains(QStringLiteral("%quality"))) {
profileElement.setAttribute(QStringLiteral("defaultquality"), item->data(0, DefaultBitrateRole).toString());
profileElement.setAttribute(QStringLiteral("qualities"), item->data(0, BitratesRole).toStringList().join(QLatin1Char(',')));
}
if (params.contains(QStringLiteral("%audiobitrate"))) {
// profile has a variable bitrate
profileElement.setAttribute(QStringLiteral("defaultaudiobitrate"), item->data(0, DefaultAudioBitrateRole).toString());
profileElement.setAttribute(QStringLiteral("audiobitrates"), item->data(0, AudioBitratesRole).toStringList().join(QLatin1Char(',')));
} else if (params.contains(QStringLiteral("%audioquality"))) {
// profile has a variable bitrate
profileElement.setAttribute(QStringLiteral("defaultaudioquality"), item->data(0, DefaultAudioBitrateRole).toString());
profileElement.setAttribute(QStringLiteral("audioqualities"), item->data(0, AudioBitratesRole).toStringList().join(QLatin1Char(',')));
}
if (item->data(0, SpeedsRole).canConvert(QVariant::StringList) && (item->data(0, SpeedsRole).toStringList().count() != 0)) {
// profile has a variable speed
profileElement.setAttribute(QStringLiteral("speeds"), item->data(0, SpeedsRole).toStringList().join(QLatin1Char(';')));
}
doc.appendChild(profileElement);
if (saveProfile(doc.documentElement())) {
parseProfiles(profileElement.attribute(QStringLiteral("name")));
}
}
void RenderWidget::slotDownloadNewRenderProfiles()
{
if (getNewStuff(QStringLiteral(":data/kdenlive_renderprofiles.knsrc")) > 0) {
reloadProfiles();
}
}
int RenderWidget::getNewStuff(const QString &configFile)
{
KNS3::Entry::List entries;
QPointer dialog = new KNS3::DownloadDialog(configFile);
if (dialog->exec() != 0) {
entries = dialog->changedEntries();
}
for (const KNS3::Entry &entry : entries) {
if (entry.status() == KNS3::Entry::Installed) {
qCDebug(KDENLIVE_LOG) << "// Installed files: " << entry.installedFiles();
}
}
delete dialog;
return entries.size();
}
void RenderWidget::slotEditProfile()
{
QTreeWidgetItem *item = m_view.formats->currentItem();
if ((item == nullptr) || (item->parent() == nullptr)) {
return;
}
QString params = item->data(0, ParamsRole).toString();
Ui::SaveProfile_UI ui;
QPointer d = new QDialog(this);
ui.setupUi(d);
QString customGroup = item->parent()->text(0);
if (customGroup.isEmpty()) {
customGroup = i18nc("Group Name", "Custom");
}
ui.group_name->setText(customGroup);
ui.profile_name->setText(item->text(0));
ui.extension->setText(item->data(0, ExtensionRole).toString());
ui.parameters->setText(params);
ui.profile_name->setFocus();
if (params.contains(QStringLiteral("%bitrate")) || ui.parameters->toPlainText().contains(QStringLiteral("%quality"))) {
if (params.contains(QStringLiteral("%quality"))) {
ui.vbitrates_label->setText(i18n("Qualities"));
ui.default_vbitrate_label->setText(i18n("Default quality"));
} else {
ui.vbitrates_label->setText(i18n("Bitrates"));
ui.default_vbitrate_label->setText(i18n("Default bitrate"));
}
if (item->data(0, BitratesRole).canConvert(QVariant::StringList) && (item->data(0, BitratesRole).toStringList().count() != 0)) {
QStringList bitrates = item->data(0, BitratesRole).toStringList();
ui.vbitrates_list->setText(bitrates.join(QLatin1Char(',')));
if (item->data(0, DefaultBitrateRole).canConvert(QVariant::String)) {
ui.default_vbitrate->setValue(item->data(0, DefaultBitrateRole).toInt());
}
}
} else {
ui.vbitrates->setHidden(true);
}
if (params.contains(QStringLiteral("%audiobitrate")) || ui.parameters->toPlainText().contains(QStringLiteral("%audioquality"))) {
if (params.contains(QStringLiteral("%audioquality"))) {
ui.abitrates_label->setText(i18n("Qualities"));
ui.default_abitrate_label->setText(i18n("Default quality"));
} else {
ui.abitrates_label->setText(i18n("Bitrates"));
ui.default_abitrate_label->setText(i18n("Default bitrate"));
}
if (item->data(0, AudioBitratesRole).canConvert(QVariant::StringList) && (item->data(0, AudioBitratesRole).toStringList().count() != 0)) {
QStringList bitrates = item->data(0, AudioBitratesRole).toStringList();
ui.abitrates_list->setText(bitrates.join(QLatin1Char(',')));
if (item->data(0, DefaultAudioBitrateRole).canConvert(QVariant::String)) {
ui.default_abitrate->setValue(item->data(0, DefaultAudioBitrateRole).toInt());
}
}
} else {
ui.abitrates->setHidden(true);
}
if (item->data(0, SpeedsRole).canConvert(QVariant::StringList) && (item->data(0, SpeedsRole).toStringList().count() != 0)) {
QStringList speeds = item->data(0, SpeedsRole).toStringList();
ui.speeds_list->setText(speeds.join('\n'));
}
d->setWindowTitle(i18n("Edit Profile"));
if (d->exec() == QDialog::Accepted) {
slotDeleteProfile(true);
QString exportFile = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/export/customprofiles.xml");
QDomDocument doc;
QFile file(exportFile);
doc.setContent(&file, false);
file.close();
QDomElement documentElement;
QDomElement profiles = doc.documentElement();
if (profiles.isNull() || profiles.tagName() != QLatin1String("profiles")) {
doc.clear();
profiles = doc.createElement(QStringLiteral("profiles"));
profiles.setAttribute(QStringLiteral("version"), 1);
doc.appendChild(profiles);
}
int version = profiles.attribute(QStringLiteral("version"), nullptr).toInt();
if (version < 1) {
doc.clear();
profiles = doc.createElement(QStringLiteral("profiles"));
profiles.setAttribute(QStringLiteral("version"), 1);
doc.appendChild(profiles);
}
QString newProfileName = ui.profile_name->text().simplified();
QString newGroupName = ui.group_name->text().simplified();
if (newGroupName.isEmpty()) {
newGroupName = i18nc("Group Name", "Custom");
}
QDomNodeList profilelist = doc.elementsByTagName(QStringLiteral("profile"));
int i = 0;
while (!profilelist.item(i).isNull()) {
// make sure a profile with same name doesn't exist
documentElement = profilelist.item(i).toElement();
QString profileName = documentElement.attribute(QStringLiteral("name"));
if (profileName == newProfileName) {
// a profile with that same name already exists
bool ok;
newProfileName = QInputDialog::getText(this, i18n("Profile already exists"),
i18n("This profile name already exists. Change the name if you do not want to overwrite it."),
QLineEdit::Normal, newProfileName, &ok);
if (!ok) {
return;
}
if (profileName == newProfileName) {
profiles.removeChild(profilelist.item(i));
break;
}
}
++i;
}
QDomElement profileElement = doc.createElement(QStringLiteral("profile"));
profileElement.setAttribute(QStringLiteral("name"), newProfileName);
profileElement.setAttribute(QStringLiteral("category"), newGroupName);
profileElement.setAttribute(QStringLiteral("extension"), ui.extension->text().simplified());
QString args = ui.parameters->toPlainText().simplified();
profileElement.setAttribute(QStringLiteral("args"), args);
if (args.contains(QStringLiteral("%bitrate"))) {
// profile has a variable bitrate
profileElement.setAttribute(QStringLiteral("defaultbitrate"), QString::number(ui.default_vbitrate->value()));
profileElement.setAttribute(QStringLiteral("bitrates"), ui.vbitrates_list->text());
} else if (args.contains(QStringLiteral("%quality"))) {
profileElement.setAttribute(QStringLiteral("defaultquality"), QString::number(ui.default_vbitrate->value()));
profileElement.setAttribute(QStringLiteral("qualities"), ui.vbitrates_list->text());
}
if (args.contains(QStringLiteral("%audiobitrate"))) {
// profile has a variable bitrate
profileElement.setAttribute(QStringLiteral("defaultaudiobitrate"), QString::number(ui.default_abitrate->value()));
profileElement.setAttribute(QStringLiteral("audiobitrates"), ui.abitrates_list->text());
} else if (args.contains(QStringLiteral("%audioquality"))) {
profileElement.setAttribute(QStringLiteral("defaultaudioquality"), QString::number(ui.default_abitrate->value()));
profileElement.setAttribute(QStringLiteral("audioqualities"), ui.abitrates_list->text());
}
QString speeds_list_str = ui.speeds_list->toPlainText();
if (!speeds_list_str.isEmpty()) {
// profile has a variable speed
profileElement.setAttribute(QStringLiteral("speeds"), speeds_list_str.replace('\n', ';').simplified());
}
profiles.appendChild(profileElement);
// QCString save = doc.toString().utf8();
delete d;
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
KMessageBox::error(this, i18n("Cannot write to file %1", exportFile));
return;
}
QTextStream out(&file);
out << doc.toString();
if (file.error() != QFile::NoError) {
KMessageBox::error(this, i18n("Cannot write to file %1", exportFile));
file.close();
return;
}
file.close();
parseProfiles();
} else {
delete d;
}
}
void RenderWidget::slotDeleteProfile(bool dontRefresh)
{
// TODO: delete a profile installed by KNewStuff the easy way
/*
QString edit = m_view.formats->currentItem()->data(EditableRole).toString();
if (!edit.endsWith(QLatin1String("customprofiles.xml"))) {
// This is a KNewStuff installed file, process through KNS
KNS::Engine engine(0);
if (engine.init("kdenlive_render.knsrc")) {
KNS::Entry::List entries;
}
return;
}*/
QTreeWidgetItem *item = m_view.formats->currentItem();
if ((item == nullptr) || (item->parent() == nullptr)) {
return;
}
QString currentProfile = item->text(0);
QString exportFile = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/export/customprofiles.xml");
QDomDocument doc;
QFile file(exportFile);
doc.setContent(&file, false);
file.close();
QDomElement documentElement;
QDomNodeList profiles = doc.elementsByTagName(QStringLiteral("profile"));
int i = 0;
QString profileName;
while (!profiles.item(i).isNull()) {
documentElement = profiles.item(i).toElement();
profileName = documentElement.attribute(QStringLiteral("name"));
if (profileName == currentProfile) {
doc.documentElement().removeChild(profiles.item(i));
break;
}
++i;
}
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
return;
}
QTextStream out(&file);
out << doc.toString();
if (file.error() != QFile::NoError) {
KMessageBox::error(this, i18n("Cannot write to file %1", exportFile));
file.close();
return;
}
file.close();
if (dontRefresh) {
return;
}
parseProfiles();
focusFirstVisibleItem();
}
void RenderWidget::updateButtons()
{
if ((m_view.formats->currentItem() == nullptr) || m_view.formats->currentItem()->isHidden()) {
m_view.buttonSave->setEnabled(false);
m_view.buttonDelete->setEnabled(false);
m_view.buttonEdit->setEnabled(false);
m_view.buttonRender->setEnabled(false);
m_view.buttonGenerateScript->setEnabled(false);
} else {
m_view.buttonSave->setEnabled(true);
m_view.buttonRender->setEnabled(m_view.formats->currentItem()->data(0, ErrorRole).isNull());
m_view.buttonGenerateScript->setEnabled(m_view.formats->currentItem()->data(0, ErrorRole).isNull());
QString edit = m_view.formats->currentItem()->data(0, EditableRole).toString();
if (edit.isEmpty() || !edit.endsWith(QLatin1String("customprofiles.xml"))) {
m_view.buttonDelete->setEnabled(false);
m_view.buttonEdit->setEnabled(false);
} else {
m_view.buttonDelete->setEnabled(true);
m_view.buttonEdit->setEnabled(true);
}
}
}
void RenderWidget::focusFirstVisibleItem(const QString &profile)
{
QTreeWidgetItem *item = nullptr;
if (!profile.isEmpty()) {
QList items = m_view.formats->findItems(profile, Qt::MatchExactly | Qt::MatchRecursive);
if (!items.isEmpty()) {
item = items.constFirst();
}
}
if (!item) {
// searched profile not found in any category, select 1st available profile
for (int i = 0; i < m_view.formats->topLevelItemCount(); ++i) {
item = m_view.formats->topLevelItem(i);
if (item->childCount() > 0) {
item = item->child(0);
break;
}
}
}
if (item) {
m_view.formats->setCurrentItem(item);
item->parent()->setExpanded(true);
refreshParams();
}
updateButtons();
}
void RenderWidget::slotPrepareExport(bool delayedRendering, const QString &scriptPath)
{
Q_UNUSED(scriptPath)
if (pCore->projectDuration() < 2) {
// Empty project, don't attempt to render
return;
}
if (!QFile::exists(KdenliveSettings::rendererpath())) {
KMessageBox::sorry(this, i18n("Cannot find the melt program required for rendering (part of Mlt)"));
return;
}
if (QFile::exists(m_view.out_file->url().toLocalFile())) {
if (KMessageBox::warningYesNo(this, i18n("Output file already exists. Do you want to overwrite it?")) != KMessageBox::Yes) {
return;
}
}
QString chapterFile;
if (m_view.create_chapter->isChecked()) {
chapterFile = m_view.out_file->url().toLocalFile() + QStringLiteral(".dvdchapter");
}
// mantisbt 1051
QDir dir(m_view.out_file->url().adjusted(QUrl::RemoveFilename).toLocalFile());
if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
KMessageBox::sorry(this, i18n("The directory %1, could not be created.\nPlease make sure you have the required permissions.",
m_view.out_file->url().adjusted(QUrl::RemoveFilename).toLocalFile()));
return;
}
prepareRendering(delayedRendering, chapterFile);
}
void RenderWidget::prepareRendering(bool delayedRendering, const QString &chapterFile)
{
KdenliveDoc *project = pCore->currentDoc();
// Save rendering profile to document
QMap renderProps;
renderProps.insert(QStringLiteral("rendercategory"), m_view.formats->currentItem()->parent()->text(0));
renderProps.insert(QStringLiteral("renderprofile"), m_view.formats->currentItem()->text(0));
renderProps.insert(QStringLiteral("renderurl"), m_view.out_file->url().toLocalFile());
renderProps.insert(QStringLiteral("renderzone"), QString::number(static_cast(m_view.render_zone->isChecked())));
renderProps.insert(QStringLiteral("renderguide"), QString::number(static_cast(m_view.render_guide->isChecked())));
renderProps.insert(QStringLiteral("renderstartguide"), QString::number(m_view.guide_start->currentIndex()));
renderProps.insert(QStringLiteral("renderendguide"), QString::number(m_view.guide_end->currentIndex()));
renderProps.insert(QStringLiteral("renderscanning"), QString::number(m_view.scanning_list->currentIndex()));
renderProps.insert(QStringLiteral("renderfield"), QString::number(m_view.field_order->currentIndex()));
int export_audio = 0;
if (m_view.export_audio->checkState() == Qt::Checked) {
export_audio = 2;
} else if (m_view.export_audio->checkState() == Qt::Unchecked) {
export_audio = 1;
}
renderProps.insert(QStringLiteral("renderexportaudio"), QString::number(export_audio));
renderProps.insert(QStringLiteral("renderrescale"), QString::number(static_cast(m_view.rescale->isChecked())));
renderProps.insert(QStringLiteral("renderrescalewidth"), QString::number(m_view.rescale_width->value()));
renderProps.insert(QStringLiteral("renderrescaleheight"), QString::number(m_view.rescale_height->value()));
renderProps.insert(QStringLiteral("rendertcoverlay"), QString::number(static_cast(m_view.tc_overlay->isChecked())));
renderProps.insert(QStringLiteral("rendertctype"), QString::number(m_view.tc_type->currentIndex()));
renderProps.insert(QStringLiteral("renderratio"), QString::number(static_cast(m_view.rescale_keep->isChecked())));
renderProps.insert(QStringLiteral("renderplay"), QString::number(static_cast(m_view.play_after->isChecked())));
renderProps.insert(QStringLiteral("rendertwopass"), QString::number(static_cast(m_view.checkTwoPass->isChecked())));
renderProps.insert(QStringLiteral("renderquality"), QString::number(m_view.video->value()));
renderProps.insert(QStringLiteral("renderaudioquality"), QString::number(m_view.audio->value()));
renderProps.insert(QStringLiteral("renderspeed"), QString::number(m_view.speed->value()));
emit selectedRenderProfile(renderProps);
QString playlistPath;
QString mltSuffix(QStringLiteral(".mlt"));
QList playlistPaths;
QList trackNames;
QString renderName;
if (delayedRendering) {
bool ok;
renderName = QFileInfo(pCore->currentDoc()->url().toLocalFile()).fileName();
if (renderName.isEmpty()) {
renderName = i18n("export") + QStringLiteral(".mlt");
} else {
renderName = renderName.section(QLatin1Char('.'), 0, -2);
renderName.append(QStringLiteral(".mlt"));
}
QDir projectFolder(pCore->currentDoc()->projectDataFolder());
projectFolder.mkpath(QStringLiteral("kdenlive-renderqueue"));
projectFolder.cd(QStringLiteral("kdenlive-renderqueue"));
if (projectFolder.exists(renderName)) {
int ix = 1;
while (projectFolder.exists(renderName)) {
if (renderName.contains(QLatin1Char('-'))) {
renderName = renderName.section(QLatin1Char('-'), 0, -2);
} else {
renderName = renderName.section(QLatin1Char('.'), 0, -2);
}
renderName.append(QString("-%1.mlt").arg(ix));
ix++;
}
}
renderName = renderName.section(QLatin1Char('.'), 0, -2);
renderName = QInputDialog::getText(this, i18n("Delayed rendering"), i18n("Select a name for this rendering."), QLineEdit::Normal, renderName, &ok);
if (!ok) {
return;
}
if (!renderName.endsWith(QStringLiteral(".mlt"))) {
renderName.append(QStringLiteral(".mlt"));
}
if (projectFolder.exists(renderName)) {
if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", renderName)) == KMessageBox::No) {
return;
}
}
playlistPath = projectFolder.absoluteFilePath(renderName);
} else {
QTemporaryFile tmp(QDir::tempPath() + "/kdenlive-XXXXXX.mlt");
if (!tmp.open()) {
// Something went wrong
return;
}
tmp.close();
playlistPath = tmp.fileName();
}
int in = 0;
int out;
Monitor *pMon = pCore->getMonitor(Kdenlive::ProjectMonitor);
bool zoneOnly = m_view.render_zone->isChecked();
if (zoneOnly) {
in = pMon->getZoneStart();
out = pMon->getZoneEnd() - 1;
} else {
out = pCore->projectDuration() - 2;
}
QString playlistContent = pCore->projectManager()->projectSceneList(project->url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile());
if (!chapterFile.isEmpty()) {
QDomDocument doc;
QDomElement chapters = doc.createElement(QStringLiteral("chapters"));
chapters.setAttribute(QStringLiteral("fps"), pCore->getCurrentFps());
doc.appendChild(chapters);
const QList guidesList = project->getGuideModel()->getAllMarkers();
for (int i = 0; i < guidesList.count(); i++) {
const CommentedTime &c = guidesList.at(i);
int time = c.time().frames(pCore->getCurrentFps());
if (time >= in && time < out) {
if (zoneOnly) {
time = time - in;
}
}
QDomElement chapter = doc.createElement(QStringLiteral("chapter"));
chapters.appendChild(chapter);
chapter.setAttribute(QStringLiteral("title"), c.comment());
chapter.setAttribute(QStringLiteral("time"), time);
}
if (!chapters.childNodes().isEmpty()) {
if (!project->getGuideModel()->hasMarker(out)) {
// Always insert a guide in pos 0
QDomElement chapter = doc.createElement(QStringLiteral("chapter"));
chapters.insertBefore(chapter, QDomNode());
chapter.setAttribute(QStringLiteral("title"), i18nc("the first in a list of chapters", "Start"));
chapter.setAttribute(QStringLiteral("time"), QStringLiteral("0"));
}
// save chapters file
QFile file(chapterFile);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qCWarning(KDENLIVE_LOG) << "////// ERROR writing DVD CHAPTER file: " << chapterFile;
} else {
file.write(doc.toString().toUtf8());
if (file.error() != QFile::NoError) {
qCWarning(KDENLIVE_LOG) << "////// ERROR writing DVD CHAPTER file: " << chapterFile;
}
file.close();
}
}
}
// Set playlist audio volume to 100%
QDomDocument doc;
doc.setContent(playlistContent);
QDomElement tractor = doc.documentElement().firstChildElement(QStringLiteral("tractor"));
if (!tractor.isNull()) {
QDomNodeList props = tractor.elementsByTagName(QStringLiteral("property"));
for (int i = 0; i < props.count(); ++i) {
if (props.at(i).toElement().attribute(QStringLiteral("name")) == QLatin1String("meta.volume")) {
props.at(i).firstChild().setNodeValue(QStringLiteral("1"));
break;
}
}
}
// Add autoclose to playlists.
QDomNodeList playlists = doc.elementsByTagName(QStringLiteral("playlist"));
for (int i = 0; i < playlists.length(); ++i) {
playlists.item(i).toElement().setAttribute(QStringLiteral("autoclose"), 1);
}
// Do we want proxy rendering
if (project->useProxy() && !proxyRendering()) {
QString root = doc.documentElement().attribute(QStringLiteral("root"));
if (!root.isEmpty() && !root.endsWith(QLatin1Char('/'))) {
root.append(QLatin1Char('/'));
}
// replace proxy clips with originals
QMap proxies = pCore->projectItemModel()->getProxies(pCore->currentDoc()->documentRoot());
QDomNodeList producers = doc.elementsByTagName(QStringLiteral("producer"));
QString producerResource;
QString producerService;
QString suffix;
QString prefix;
for (int n = 0; n < producers.length(); ++n) {
QDomElement e = producers.item(n).toElement();
producerResource = Xml::getXmlProperty(e, QStringLiteral("resource"));
producerService = Xml::getXmlProperty(e, QStringLiteral("mlt_service"));
if (producerResource.isEmpty() || producerService == QLatin1String("color")) {
continue;
}
if (producerService == QLatin1String("timewarp")) {
// slowmotion producer
prefix = producerResource.section(QLatin1Char(':'), 0, 0) + QLatin1Char(':');
producerResource = producerResource.section(QLatin1Char(':'), 1);
} else {
prefix.clear();
}
if (producerService == QLatin1String("framebuffer")) {
// slowmotion producer
suffix = QLatin1Char('?') + producerResource.section(QLatin1Char('?'), 1);
producerResource = producerResource.section(QLatin1Char('?'), 0, 0);
} else {
suffix.clear();
}
if (!producerResource.isEmpty()) {
if (QFileInfo(producerResource).isRelative()) {
producerResource.prepend(root);
}
if (proxies.contains(producerResource)) {
QString replacementResource = proxies.value(producerResource);
Xml::setXmlProperty(e, QStringLiteral("resource"), prefix + replacementResource + suffix);
if (producerService == QLatin1String("timewarp")) {
Xml::setXmlProperty(e, QStringLiteral("warp_resource"), replacementResource);
}
// We need to delete the "aspect_ratio" property because proxy clips
// sometimes have different ratio than original clips
Xml::removeXmlProperty(e, QStringLiteral("aspect_ratio"));
Xml::removeMetaProperties(e);
}
}
}
}
generateRenderFiles(doc, playlistPath, in, out, delayedRendering);
}
void RenderWidget::generateRenderFiles(QDomDocument doc, const QString &playlistPath, int in, int out, bool delayedRendering)
{
QDomDocument clone;
KdenliveDoc *project = pCore->currentDoc();
int passes = m_view.checkTwoPass->isChecked() ? 2 : 1;
QString renderArgs = m_view.advanced_params->toPlainText().simplified();
QDomElement consumer = doc.createElement(QStringLiteral("consumer"));
QDomNodeList profiles = doc.elementsByTagName(QStringLiteral("profile"));
if (profiles.isEmpty()) {
doc.documentElement().insertAfter(consumer, doc.documentElement());
} else {
doc.documentElement().insertAfter(consumer, profiles.at(profiles.length() - 1));
}
std::unique_ptr &profile = pCore->getCurrentProfile();
if (renderArgs.contains(QLatin1String("%dv_standard"))) {
QString dvstd;
if (fmod(double(profile->frame_rate_num() / profile->frame_rate_den()), 30.01) > 27) {
dvstd = QStringLiteral("ntsc");
} else {
dvstd = QStringLiteral("pal");
}
if (double(profile->display_aspect_num() / profile->display_aspect_den()) > 1.5) {
dvstd += QLatin1String("_wide");
}
renderArgs.replace(QLatin1String("%dv_standard"), dvstd);
}
QStringList args = renderArgs.split(QLatin1Char(' '));
for (auto ¶m : args) {
if (param.contains(QLatin1Char('='))) {
QString paramValue = param.section(QLatin1Char('='), 1);
if (paramValue.startsWith(QLatin1Char('%'))) {
if (paramValue.startsWith(QStringLiteral("%bitrate")) || paramValue == QStringLiteral("%quality")) {
if (paramValue.contains("+'k'"))
paramValue = QString::number(m_view.video->value()) + 'k';
else
paramValue = QString::number(m_view.video->value());
}
if (paramValue.startsWith(QStringLiteral("%audiobitrate")) || paramValue == QStringLiteral("%audioquality")) {
if (paramValue.contains("+'k'"))
paramValue = QString::number(m_view.audio->value()) + 'k';
else
paramValue = QString::number(m_view.audio->value());
}
if (paramValue == QStringLiteral("%dar"))
paramValue = '@' + QString::number(profile->display_aspect_num()) + QLatin1Char('/') + QString::number(profile->display_aspect_den());
if (paramValue == QStringLiteral("%passes")) paramValue = QString::number(static_cast(m_view.checkTwoPass->isChecked()) + 1);
}
consumer.setAttribute(param.section(QLatin1Char('='), 0, 0), paramValue);
}
}
// Check for movit
if (KdenliveSettings::gpu_accel()) {
consumer.setAttribute(QStringLiteral("glsl."), 1);
}
// in/out points
if (m_view.render_guide->isChecked()) {
double fps = profile->fps();
double guideStart = m_view.guide_start->itemData(m_view.guide_start->currentIndex()).toDouble();
double guideEnd = m_view.guide_end->itemData(m_view.guide_end->currentIndex()).toDouble();
consumer.setAttribute(QStringLiteral("in"), int(GenTime(guideStart).frames(fps)));
consumer.setAttribute(QStringLiteral("out"), int(GenTime(guideEnd).frames(fps)));
} else {
consumer.setAttribute(QStringLiteral("in"), in);
consumer.setAttribute(QStringLiteral("out"), out);
}
// Check if the rendering profile is different from project profile,
// in which case we need to use the producer_comsumer from MLT
QString subsize;
if (renderArgs.startsWith(QLatin1String("s="))) {
subsize = renderArgs.section(QLatin1Char(' '), 0, 0).toLower();
subsize = subsize.section(QLatin1Char('='), 1, 1);
} else if (renderArgs.contains(QStringLiteral(" s="))) {
subsize = renderArgs.section(QStringLiteral(" s="), 1, 1);
subsize = subsize.section(QLatin1Char(' '), 0, 0).toLower();
} else if (m_view.rescale->isChecked() && m_view.rescale->isEnabled()) {
subsize = QStringLiteral("%1x%2").arg(m_view.rescale_width->value()).arg(m_view.rescale_height->value());
}
if (!subsize.isEmpty()) {
consumer.setAttribute(QStringLiteral("s"), subsize);
}
// Project metadata
if (m_view.export_meta->isChecked()) {
QMap metadata = project->metadata();
QMap::const_iterator i = metadata.constBegin();
while (i != metadata.constEnd()) {
consumer.setAttribute(i.key(), i.value());
++i;
}
}
// Adjust encoding speed
if (m_view.speed->isEnabled() && m_view.formats->currentItem()) {
QStringList speeds = m_view.formats->currentItem()->data(0, SpeedsRole).toStringList();
if (m_view.speed->value() < speeds.count()) {
QString speedValue = speeds.at(m_view.speed->value());
if (speedValue.contains(QLatin1Char('='))) {
consumer.setAttribute(speedValue.section(QLatin1Char('='), 0, 0), speedValue.section(QLatin1Char('='), 1));
}
}
}
// Adjust scanning
switch (m_view.scanning_list->currentIndex()) {
case 1:
consumer.setAttribute(QStringLiteral("progressive"), 1);
break;
case 2:
// Interlaced rendering
consumer.setAttribute(QStringLiteral("progressive"), 0);
// Adjust field order
consumer.setAttribute(QStringLiteral("top_field_first"), m_view.field_order->currentIndex());
break;
default:
// leave as is
break;
}
// check if audio export is selected
bool exportAudio;
if (automaticAudioExport()) {
// TODO check if projact contains audio
// exportAudio = pCore->projectManager()->currentTimeline()->checkProjectAudio();
exportAudio = true;
} else {
exportAudio = selectedAudioExport();
}
if (renderArgs.contains(QLatin1String("pix_fmt=argb"))
|| renderArgs.contains(QLatin1String("pix_fmt=abgr"))
|| renderArgs.contains(QLatin1String("pix_fmt=bgra"))
|| renderArgs.contains(QLatin1String("pix_fmt=gbra"))
|| renderArgs.contains(QLatin1String("pix_fmt=rgba"))
|| renderArgs.contains(QLatin1String("pix_fmt=yuva"))
|| renderArgs.contains(QLatin1String("pix_fmt=ya" ))
|| renderArgs.contains(QLatin1String("pix_fmt=ayuv"))) {
auto prods = doc.elementsByTagName(QStringLiteral("producer"));
for (int i = 0; i < prods.count(); ++i) {
auto prod = prods.at(i).toElement();
if (prod.attribute(QStringLiteral("id")) == QStringLiteral("black_track")) {
Xml::setXmlProperty(prod, QStringLiteral("resource"), QStringLiteral("transparent"));
break;
}
}
}
// disable audio if requested
if (!exportAudio) {
consumer.setAttribute(QStringLiteral("an"), 1);
}
int threadCount = QThread::idealThreadCount();
if (threadCount < 2 || !m_view.parallel_process->isChecked() || !m_view.parallel_process->isEnabled()) {
threadCount = 1;
} else {
threadCount = qMin(4, threadCount - 1);
}
// Set the thread counts
if (!renderArgs.contains(QStringLiteral("threads="))) {
consumer.setAttribute(QStringLiteral("threads"), KdenliveSettings::encodethreads());
}
consumer.setAttribute(QStringLiteral("real_time"), -threadCount);
// check which audio tracks have to be exported
/*if (stemExport) {
// TODO refac
//TODO port to new timeline model
Timeline *ct = pCore->projectManager()->currentTimeline();
int allTracksCount = ct->tracksCount();
// reset tracks count (tracks to be rendered)
tracksCount = 0;
// begin with track 1 (track zero is a hidden black track)
for (int i = 1; i < allTracksCount; i++) {
Track *track = ct->track(i);
// add only tracks to render list that are not muted and have audio
if ((track != nullptr) && !track->info().isMute && track->hasAudio()) {
QDomDocument docCopy = doc.cloneNode(true).toDocument();
QString trackName = track->info().trackName;
// save track name
trackNames << trackName;
qCDebug(KDENLIVE_LOG) << "Track-Name: " << trackName;
// create stem export doc content
QDomNodeList tracks = docCopy.elementsByTagName(QStringLiteral("track"));
for (int j = 0; j < allTracksCount; j++) {
if (j != i) {
// mute other tracks
tracks.at(j).toElement().setAttribute(QStringLiteral("hide"), QStringLiteral("both"));
}
}
docList << docCopy;
tracksCount++;
}
}
}*/
if (m_view.checkTwoPass->isChecked()) {
// We will generate 2 files, one for each pass.
clone = doc.cloneNode(true).toDocument();
}
QStringList playlists;
QString renderedFile = m_view.out_file->url().toLocalFile();
for (int i = 0; i < passes; i++) {
// Append consumer settings
QDomDocument final = i > 0 ? clone : doc;
QDomNodeList cons = final.elementsByTagName(QStringLiteral("consumer"));
QDomElement myConsumer = cons.at(0).toElement();
QString mytarget = renderedFile;
QString playlistName = playlistPath;
myConsumer.setAttribute(QStringLiteral("mlt_service"), QStringLiteral("avformat"));
if (passes == 2 && i == 1) {
playlistName = playlistName.section(QLatin1Char('.'), 0, -2) + QString("-pass%1.").arg(i + 1) + playlistName.section(QLatin1Char('.'), -1);
}
playlists << playlistName;
myConsumer.setAttribute(QStringLiteral("target"), mytarget);
// Prepare rendering args
int pass = passes == 2 ? i + 1 : 0;
if (renderArgs.contains(QStringLiteral("libx265"))) {
if (pass == 1 || pass == 2) {
QString x265params = myConsumer.attribute("x265-params");
x265params = QString("pass=%1:stats=%2:%3").arg(pass).arg(mytarget.replace(":", "\\:") + "_2pass.log").arg(x265params);
myConsumer.setAttribute("x265-params", x265params);
}
} else {
if (pass == 1 || pass == 2) {
myConsumer.setAttribute("pass", pass);
myConsumer.setAttribute("passlogfile", mytarget + "_2pass.log");
}
if (pass == 1) {
myConsumer.setAttribute("fastfirstpass", 1);
myConsumer.removeAttribute("acodec");
myConsumer.setAttribute("an", 1);
} else {
myConsumer.removeAttribute("fastfirstpass");
}
}
QFile file(playlistName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
pCore->displayMessage(i18n("Cannot write to file %1", playlistName), ErrorMessage);
return;
}
file.write(final.toString().toUtf8());
if (file.error() != QFile::NoError) {
pCore->displayMessage(i18n("Cannot write to file %1", playlistName), ErrorMessage);
file.close();
return;
}
file.close();
}
// Create job
RenderJobItem *renderItem = nullptr;
QList existing = m_view.running_jobs->findItems(renderedFile, Qt::MatchExactly, 1);
if (!existing.isEmpty()) {
renderItem = static_cast(existing.at(0));
if (renderItem->status() == RUNNINGJOB || renderItem->status() == WAITINGJOB || renderItem->status() == STARTINGJOB) {
KMessageBox::information(
this, i18n("There is already a job writing file:%1 Abort the job if you want to overwrite it...", renderedFile),
i18n("Already running"));
return;
}
if (delayedRendering || playlists.size() > 1) {
delete renderItem;
renderItem = nullptr;
} else {
renderItem->setData(1, ProgressRole, 0);
renderItem->setStatus(WAITINGJOB);
renderItem->setIcon(0, QIcon::fromTheme(QStringLiteral("media-playback-pause")));
renderItem->setData(1, Qt::UserRole, i18n("Waiting..."));
QStringList argsJob = {KdenliveSettings::rendererpath(), playlistPath, renderedFile,
QStringLiteral("-pid:%1").arg(QCoreApplication::applicationPid())};
renderItem->setData(1, ParametersRole, argsJob);
renderItem->setData(1, TimeRole, QDateTime::currentDateTime());
if (!exportAudio) {
renderItem->setData(1, ExtraInfoRole, i18n("Video without audio track"));
} else {
renderItem->setData(1, ExtraInfoRole, QString());
}
m_view.running_jobs->setCurrentItem(renderItem);
m_view.tabWidget->setCurrentIndex(1);
checkRenderStatus();
return;
}
}
if (delayedRendering) {
parseScriptFiles();
return;
}
QList jobList;
for (const QString &pl : playlists) {
renderItem = new RenderJobItem(m_view.running_jobs, QStringList() << QString() << renderedFile);
renderItem->setData(1, TimeRole, QDateTime::currentDateTime());
QStringList argsJob = {KdenliveSettings::rendererpath(), pl, renderedFile, QStringLiteral("-pid:%1").arg(QCoreApplication::applicationPid())};
renderItem->setData(1, ParametersRole, argsJob);
qDebug() << "* CREATED JOB WITH ARGS: " << argsJob;
if (!exportAudio) {
renderItem->setData(1, ExtraInfoRole, i18n("Video without audio track"));
} else {
renderItem->setData(1, ExtraInfoRole, QString());
}
jobList << renderItem;
}
m_view.running_jobs->setCurrentItem(jobList.at(0));
m_view.tabWidget->setCurrentIndex(1);
// check render status
checkRenderStatus();
// create full playlistPaths
/*for (int i = 0; i < tracksCount; i++) {
QString plPath(playlistPath);
// add track number to path name
if (stemExport) {
plPath = plPath + QLatin1Char('_') + QString(trackNames.at(i)).replace(QLatin1Char(' '), QLatin1Char('_'));
}
// add mlt suffix
if (!plPath.endsWith(mltSuffix)) {
plPath += mltSuffix;
}
playlistPaths << plPath;
qCDebug(KDENLIVE_LOG) << "playlistPath: " << plPath << endl;
// Do save scenelist
QFile file(plPath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
pCore->displayMessage(i18n("Cannot write to file %1", plPath), ErrorMessage);
return;
}
file.write(docList.at(i).toString().toUtf8());
if (file.error() != QFile::NoError) {
pCore->displayMessage(i18n("Cannot write to file %1", plPath), ErrorMessage);
file.close();
return;
}
file.close();
}*/
// slotExport(delayedRendering, in, out, project->metadata(), playlistPaths, trackNames, renderName, exportAudio);
}
void RenderWidget::checkRenderStatus()
{
// check if we have a job waiting to render
if (m_blockProcessing) {
return;
}
auto *item = static_cast(m_view.running_jobs->topLevelItem(0));
// Make sure no other rendering is running
while (item != nullptr) {
if (item->status() == RUNNINGJOB) {
return;
}
item = static_cast(m_view.running_jobs->itemBelow(item));
}
item = static_cast(m_view.running_jobs->topLevelItem(0));
bool waitingJob = false;
// Find first waiting job
while (item != nullptr) {
if (item->status() == WAITINGJOB) {
item->setData(1, TimeRole, QDateTime::currentDateTime());
waitingJob = true;
startRendering(item);
// Check for 2 pass encoding
QStringList jobData = item->data(1, ParametersRole).toStringList();
if (jobData.size() > 2 && jobData.at(1).endsWith(QStringLiteral("-pass2.mlt"))) {
// Find and remove 1st pass job
QTreeWidgetItem *above = m_view.running_jobs->itemAbove(item);
QString firstPassName = jobData.at(1).section(QLatin1Char('-'), 0, -2) + QStringLiteral(".mlt");
while (above) {
QStringList aboveData = above->data(1, ParametersRole).toStringList();
qDebug() << "// GOT JOB: " << aboveData.at(1);
if (aboveData.size() > 2 && aboveData.at(1) == firstPassName) {
delete above;
break;
}
above = m_view.running_jobs->itemAbove(above);
}
}
item->setStatus(STARTINGJOB);
break;
}
item = static_cast(m_view.running_jobs->itemBelow(item));
}
if (!waitingJob && m_view.shutdown->isChecked()) {
emit shutdown();
}
}
void RenderWidget::startRendering(RenderJobItem *item)
{
auto rendererArgs = item->data(1, ParametersRole).toStringList();
qDebug() << "starting kdenlive_render process using: " << m_renderer;
if (!QProcess::startDetached(m_renderer, rendererArgs)) {
item->setStatus(FAILEDJOB);
} else {
KNotification::event(QStringLiteral("RenderStarted"), i18n("Rendering %1 started", item->text(1)), QPixmap(), this);
}
}
int RenderWidget::waitingJobsCount() const
{
int count = 0;
auto *item = static_cast(m_view.running_jobs->topLevelItem(0));
while (item != nullptr) {
if (item->status() == WAITINGJOB || item->status() == STARTINGJOB) {
count++;
}
item = static_cast(m_view.running_jobs->itemBelow(item));
}
return count;
}
void RenderWidget::adjustViewToProfile()
{
m_view.scanning_list->setCurrentIndex(0);
m_view.rescale_width->setValue(KdenliveSettings::defaultrescalewidth());
if (!m_view.rescale_keep->isChecked()) {
m_view.rescale_height->blockSignals(true);
m_view.rescale_height->setValue(KdenliveSettings::defaultrescaleheight());
m_view.rescale_height->blockSignals(false);
}
refreshView();
}
void RenderWidget::refreshView()
{
m_view.formats->blockSignals(true);
QIcon brokenIcon = QIcon::fromTheme(QStringLiteral("dialog-close"));
QIcon warningIcon = QIcon::fromTheme(QStringLiteral("dialog-warning"));
KColorScheme scheme(palette().currentColorGroup(), KColorScheme::Window);
const QColor disabled = scheme.foreground(KColorScheme::InactiveText).color();
const QColor disabledbg = scheme.background(KColorScheme::NegativeBackground).color();
// We borrow a reference to the profile's pointer to query it more easily
std::unique_ptr &profile = pCore->getCurrentProfile();
double project_framerate = double(profile->frame_rate_num()) / profile->frame_rate_den();
for (int i = 0; i < m_view.formats->topLevelItemCount(); ++i) {
QTreeWidgetItem *group = m_view.formats->topLevelItem(i);
for (int j = 0; j < group->childCount(); ++j) {
QTreeWidgetItem *item = group->child(j);
QString std = item->data(0, StandardRole).toString();
if (std.isEmpty() ||
(std.contains(QStringLiteral("PAL"), Qt::CaseInsensitive) && profile->frame_rate_num() == 25 && profile->frame_rate_den() == 1) ||
(std.contains(QStringLiteral("NTSC"), Qt::CaseInsensitive) && profile->frame_rate_num() == 30000 && profile->frame_rate_den() == 1001)) {
// Standard OK
} else {
item->setData(0, ErrorRole, i18n("Standard (%1) not compatible with project profile (%2)", std, project_framerate));
item->setIcon(0, brokenIcon);
item->setForeground(0, disabled);
continue;
}
QString params = item->data(0, ParamsRole).toString();
// Make sure the selected profile uses the same frame rate as project profile
if (params.contains(QStringLiteral("mlt_profile="))) {
QString profile_str = params.section(QStringLiteral("mlt_profile="), 1, 1).section(QLatin1Char(' '), 0, 0);
std::unique_ptr &target_profile = ProfileRepository::get()->getProfile(profile_str);
if (target_profile->frame_rate_den() > 0) {
double profile_rate = double(target_profile->frame_rate_num()) / target_profile->frame_rate_den();
if (int(1000.0 * profile_rate) != int(1000.0 * project_framerate)) {
item->setData(0, ErrorRole, i18n("Frame rate (%1) not compatible with project profile (%2)", profile_rate, project_framerate));
item->setIcon(0, brokenIcon);
item->setForeground(0, disabled);
continue;
}
}
}
// Make sure the selected profile uses an installed avformat codec / format
if (!supportedFormats.isEmpty()) {
QString format;
if (params.startsWith(QLatin1String("f="))) {
format = params.section(QStringLiteral("f="), 1, 1);
} else if (params.contains(QStringLiteral(" f="))) {
format = params.section(QStringLiteral(" f="), 1, 1);
}
if (!format.isEmpty()) {
format = format.section(QLatin1Char(' '), 0, 0).toLower();
if (!supportedFormats.contains(format)) {
item->setData(0, ErrorRole, i18n("Unsupported video format: %1", format));
item->setIcon(0, brokenIcon);
item->setForeground(0, disabled);
continue;
}
}
}
if (!acodecsList.isEmpty()) {
QString format;
if (params.startsWith(QLatin1String("acodec="))) {
format = params.section(QStringLiteral("acodec="), 1, 1);
} else if (params.contains(QStringLiteral(" acodec="))) {
format = params.section(QStringLiteral(" acodec="), 1, 1);
}
if (!format.isEmpty()) {
format = format.section(QLatin1Char(' '), 0, 0).toLower();
if (!acodecsList.contains(format)) {
item->setData(0, ErrorRole, i18n("Unsupported audio codec: %1", format));
item->setIcon(0, brokenIcon);
item->setForeground(0, disabled);
item->setBackground(0, disabledbg);
}
}
}
if (!vcodecsList.isEmpty()) {
QString format;
if (params.startsWith(QLatin1String("vcodec="))) {
format = params.section(QStringLiteral("vcodec="), 1, 1);
} else if (params.contains(QStringLiteral(" vcodec="))) {
format = params.section(QStringLiteral(" vcodec="), 1, 1);
}
if (!format.isEmpty()) {
format = format.section(QLatin1Char(' '), 0, 0).toLower();
if (!vcodecsList.contains(format)) {
item->setData(0, ErrorRole, i18n("Unsupported video codec: %1", format));
item->setIcon(0, brokenIcon);
item->setForeground(0, disabled);
continue;
}
}
}
if (params.contains(QStringLiteral(" profile=")) || params.startsWith(QLatin1String("profile="))) {
// changed in MLT commit d8a3a5c9190646aae72048f71a39ee7446a3bd45
// (https://github.com/mltframework/mlt/commit/d8a3a5c9190646aae72048f71a39ee7446a3bd45)
item->setData(0, ErrorRole,
i18n("This render profile uses a 'profile' parameter. Unless you know what you are doing you will probably "
"have to change it to 'mlt_profile'."));
item->setIcon(0, warningIcon);
continue;
}
}
}
focusFirstVisibleItem();
m_view.formats->blockSignals(false);
refreshParams();
}
QUrl RenderWidget::filenameWithExtension(QUrl url, const QString &extension)
{
if (!url.isValid()) {
url = QUrl::fromLocalFile(pCore->currentDoc()->projectDataFolder() + QDir::separator());
}
QString directory = url.adjusted(QUrl::RemoveFilename).toLocalFile();
QString filename = url.fileName();
if (filename.isEmpty()) {
filename = pCore->currentDoc()->url().fileName();
}
QString ext;
if (extension.at(0) == '.') {
ext = extension;
} else {
ext = '.' + extension;
}
if (filename.isEmpty()) {
filename = i18n("untitled");
}
int pos = filename.lastIndexOf('.');
if (pos == 0) {
filename.append(ext);
} else {
if (!filename.endsWith(ext, Qt::CaseInsensitive)) {
filename = filename.left(pos) + ext;
}
}
return QUrl::fromLocalFile(directory + filename);
}
void RenderWidget::refreshParams()
{
// Format not available (e.g. codec not installed); Disable start button
QTreeWidgetItem *item = m_view.formats->currentItem();
if ((item == nullptr) || item->parent() == nullptr) {
// This is a category item, not a real profile
m_view.buttonBox->setEnabled(false);
} else {
m_view.buttonBox->setEnabled(true);
}
QString extension;
if (item) {
extension = item->data(0, ExtensionRole).toString();
}
if ((item == nullptr) || item->isHidden() || extension.isEmpty()) {
if (!item) {
errorMessage(ProfileError, i18n("No matching profile"));
} else if (!item->parent()) // category
;
else if (extension.isEmpty()) {
errorMessage(ProfileError, i18n("Invalid profile"));
}
m_view.advanced_params->clear();
m_view.buttonRender->setEnabled(false);
m_view.buttonGenerateScript->setEnabled(false);
return;
}
QString params = item->data(0, ParamsRole).toString();
errorMessage(ProfileError, item->data(0, ErrorRole).toString());
m_view.advanced_params->setPlainText(params);
if (params.contains(QStringLiteral(" s=")) || params.startsWith(QLatin1String("s=")) || params.contains(QLatin1String("%dv_standard"))) {
// profile has a fixed size, do not allow resize
m_view.rescale->setEnabled(false);
setRescaleEnabled(false);
} else {
m_view.rescale->setEnabled(true);
setRescaleEnabled(m_view.rescale->isChecked());
}
QUrl url = filenameWithExtension(m_view.out_file->url(), extension);
m_view.out_file->setUrl(url);
// if (!url.isEmpty()) {
// QString path = url.path();
// int pos = path.lastIndexOf('.') + 1;
// if (pos == 0) path.append('.' + extension);
// else path = path.left(pos) + extension;
// m_view.out_file->setUrl(QUrl(path));
// } else {
// m_view.out_file->setUrl(QUrl(QDir::homePath() + QStringLiteral("/untitled.") + extension));
// }
m_view.out_file->setFilter("*." + extension);
QString edit = item->data(0, EditableRole).toString();
if (edit.isEmpty() || !edit.endsWith(QLatin1String("customprofiles.xml"))) {
m_view.buttonDelete->setEnabled(false);
m_view.buttonEdit->setEnabled(false);
} else {
m_view.buttonDelete->setEnabled(true);
m_view.buttonEdit->setEnabled(true);
}
// video quality control
m_view.video->blockSignals(true);
bool quality = false;
if ((params.contains(QStringLiteral("%quality")) || params.contains(QStringLiteral("%bitrate"))) &&
item->data(0, BitratesRole).canConvert(QVariant::StringList)) {
// bitrates or quantizers list
QStringList qs = item->data(0, BitratesRole).toStringList();
if (qs.count() > 1) {
quality = true;
int qmax = qs.constFirst().toInt();
int qmin = qs.last().toInt();
if (qmax < qmin) {
// always show best quality on right
m_view.video->setRange(qmax, qmin);
m_view.video->setProperty("decreasing", true);
} else {
m_view.video->setRange(qmin, qmax);
m_view.video->setProperty("decreasing", false);
}
}
}
m_view.video->setEnabled(quality);
m_view.quality->setEnabled(quality);
m_view.qualityLabel->setEnabled(quality);
m_view.video->blockSignals(false);
// audio quality control
quality = false;
m_view.audio->blockSignals(true);
if ((params.contains(QStringLiteral("%audioquality")) || params.contains(QStringLiteral("%audiobitrate"))) &&
item->data(0, AudioBitratesRole).canConvert(QVariant::StringList)) {
// bitrates or quantizers list
QStringList qs = item->data(0, AudioBitratesRole).toStringList();
if (qs.count() > 1) {
quality = true;
int qmax = qs.constFirst().toInt();
int qmin = qs.last().toInt();
if (qmax < qmin) {
m_view.audio->setRange(qmax, qmin);
m_view.audio->setProperty("decreasing", true);
} else {
m_view.audio->setRange(qmin, qmax);
m_view.audio->setProperty("decreasing", false);
}
if (params.contains(QStringLiteral("%audiobitrate"))) {
m_view.audio->setSingleStep(32); // 32kbps step
} else {
m_view.audio->setSingleStep(1);
}
}
}
m_view.audio->setEnabled(quality);
m_view.audio->blockSignals(false);
if (m_view.quality->isEnabled()) {
adjustAVQualities(m_view.quality->value());
}
if (item->data(0, SpeedsRole).canConvert(QVariant::StringList) && (item->data(0, SpeedsRole).toStringList().count() != 0)) {
int speed = item->data(0, SpeedsRole).toStringList().count() - 1;
m_view.speed->setEnabled(true);
m_view.speed->setMaximum(speed);
m_view.speed->setValue(speed * 3 / 4); // default to intermediate speed
} else {
m_view.speed->setEnabled(false);
}
if (!item->data(0, FieldRole).isNull()) {
m_view.field_order->setCurrentIndex(item->data(0, FieldRole).toInt());
}
adjustSpeed(m_view.speed->value());
bool passes = params.contains(QStringLiteral("passes"));
m_view.checkTwoPass->setEnabled(passes);
m_view.checkTwoPass->setChecked(passes && params.contains(QStringLiteral("passes=2")));
m_view.encoder_threads->setEnabled(!params.contains(QStringLiteral("threads=")));
m_view.buttonRender->setEnabled(m_view.formats->currentItem()->data(0, ErrorRole).isNull());
m_view.buttonGenerateScript->setEnabled(m_view.formats->currentItem()->data(0, ErrorRole).isNull());
}
void RenderWidget::reloadProfiles()
{
parseProfiles();
}
void RenderWidget::parseProfiles(const QString &selectedProfile)
{
m_view.formats->clear();
// Parse our xml profile
QString exportFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("export/profiles.xml"));
parseFile(exportFile, false);
// Parse some MLT's profiles
parseMltPresets();
QString exportFolder = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/export/");
QDir directory(exportFolder);
QStringList filter;
filter << QStringLiteral("*.xml");
QStringList fileList = directory.entryList(filter, QDir::Files);
// We should parse customprofiles.xml in last position, so that user profiles
// can also override profiles installed by KNewStuff
fileList.removeAll(QStringLiteral("customprofiles.xml"));
for (const QString &filename : fileList) {
parseFile(directory.absoluteFilePath(filename), true);
}
if (QFile::exists(exportFolder + QStringLiteral("customprofiles.xml"))) {
parseFile(exportFolder + QStringLiteral("customprofiles.xml"), true);
}
focusFirstVisibleItem(selectedProfile);
}
void RenderWidget::parseMltPresets()
{
QDir root(KdenliveSettings::mltpath());
if (!root.cd(QStringLiteral("../presets/consumer/avformat"))) {
// Cannot find MLT's presets directory
qCWarning(KDENLIVE_LOG) << " / / / WARNING, cannot find MLT's preset folder";
return;
}
if (root.cd(QStringLiteral("lossless"))) {
QString groupName = i18n("Lossless/HQ");
QList foundGroup = m_view.formats->findItems(groupName, Qt::MatchExactly);
QTreeWidgetItem *groupItem;
if (!foundGroup.isEmpty()) {
groupItem = foundGroup.takeFirst();
} else {
groupItem = new QTreeWidgetItem(QStringList(groupName));
m_view.formats->addTopLevelItem(groupItem);
groupItem->setExpanded(true);
}
const QStringList profiles = root.entryList(QDir::Files, QDir::Name);
for (const QString &prof : profiles) {
KConfig config(root.absoluteFilePath(prof), KConfig::SimpleConfig);
KConfigGroup group = config.group(QByteArray());
QString vcodec = group.readEntry("vcodec");
QString acodec = group.readEntry("acodec");
QString extension = group.readEntry("meta.preset.extension");
QString note = group.readEntry("meta.preset.note");
QString profileName = prof;
if (!vcodec.isEmpty() || !acodec.isEmpty()) {
profileName.append(" (");
if (!vcodec.isEmpty()) {
profileName.append(vcodec);
if (!acodec.isEmpty()) {
profileName.append("+" + acodec);
}
} else if (!acodec.isEmpty()) {
profileName.append(acodec);
}
profileName.append(QLatin1Char(')'));
}
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(profileName));
item->setData(0, ExtensionRole, extension);
item->setData(0, RenderRole, "avformat");
item->setData(0, ParamsRole, QString("properties=lossless/" + prof));
if (!note.isEmpty()) {
item->setToolTip(0, note);
}
groupItem->addChild(item);
}
}
if (root.cd(QStringLiteral("../stills"))) {
QString groupName = i18nc("Category Name", "Images sequence");
QList foundGroup = m_view.formats->findItems(groupName, Qt::MatchExactly);
QTreeWidgetItem *groupItem;
if (!foundGroup.isEmpty()) {
groupItem = foundGroup.takeFirst();
} else {
groupItem = new QTreeWidgetItem(QStringList(groupName));
m_view.formats->addTopLevelItem(groupItem);
groupItem->setExpanded(true);
}
QStringList profiles = root.entryList(QDir::Files, QDir::Name);
for (const QString &prof : profiles) {
QTreeWidgetItem *item = loadFromMltPreset(groupName, root.absoluteFilePath(prof), prof);
if (!item) {
continue;
}
item->setData(0, ParamsRole, QString("properties=stills/" + prof));
groupItem->addChild(item);
}
// Add GIF as image sequence
root.cdUp();
QTreeWidgetItem *item = loadFromMltPreset(groupName, root.absoluteFilePath(QStringLiteral("GIF")), QStringLiteral("GIF"));
if (item) {
item->setData(0, ParamsRole, QStringLiteral("properties=GIF"));
groupItem->addChild(item);
}
}
}
QTreeWidgetItem *RenderWidget::loadFromMltPreset(const QString &groupName, const QString &path, const QString &profileName)
{
KConfig config(path, KConfig::SimpleConfig);
KConfigGroup group = config.group(QByteArray());
QString extension = group.readEntry("meta.preset.extension");
QString note = group.readEntry("meta.preset.note");
if (extension.isEmpty()) {
return nullptr;
}
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(profileName));
item->setData(0, GroupRole, groupName);
item->setData(0, ExtensionRole, extension);
item->setData(0, RenderRole, "avformat");
if (!note.isEmpty()) {
item->setToolTip(0, note);
}
return item;
}
void RenderWidget::parseFile(const QString &exportFile, bool editable)
{
QDomDocument doc;
QFile file(exportFile);
doc.setContent(&file, false);
file.close();
QDomElement documentElement;
QDomElement profileElement;
QString extension;
QDomNodeList groups = doc.elementsByTagName(QStringLiteral("group"));
QTreeWidgetItem *item = nullptr;
bool replaceVorbisCodec = false;
if (acodecsList.contains(QStringLiteral("libvorbis"))) {
replaceVorbisCodec = true;
}
bool replaceLibfaacCodec = false;
if (acodecsList.contains(QStringLiteral("libfaac"))) {
replaceLibfaacCodec = true;
}
if (editable || groups.isEmpty()) {
QDomElement profiles = doc.documentElement();
if (editable && profiles.attribute(QStringLiteral("version"), nullptr).toInt() < 1) {
// this is an old profile version, update it
QDomDocument newdoc;
QDomElement newprofiles = newdoc.createElement(QStringLiteral("profiles"));
newprofiles.setAttribute(QStringLiteral("version"), 1);
newdoc.appendChild(newprofiles);
QDomNodeList profilelist = doc.elementsByTagName(QStringLiteral("profile"));
for (int i = 0; i < profilelist.count(); ++i) {
QString category = i18nc("Category Name", "Custom");
QString ext;
QDomNode parent = profilelist.at(i).parentNode();
if (!parent.isNull()) {
QDomElement parentNode = parent.toElement();
if (parentNode.hasAttribute(QStringLiteral("name"))) {
category = parentNode.attribute(QStringLiteral("name"));
}
ext = parentNode.attribute(QStringLiteral("extension"));
}
if (!profilelist.at(i).toElement().hasAttribute(QStringLiteral("category"))) {
profilelist.at(i).toElement().setAttribute(QStringLiteral("category"), category);
}
if (!ext.isEmpty()) {
profilelist.at(i).toElement().setAttribute(QStringLiteral("extension"), ext);
}
QDomNode n = profilelist.at(i).cloneNode();
newprofiles.appendChild(newdoc.importNode(n, true));
}
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
return;
}
QTextStream out(&file);
out << newdoc.toString();
file.close();
parseFile(exportFile, editable);
return;
}
QDomNode node = doc.elementsByTagName(QStringLiteral("profile")).at(0);
if (node.isNull()) {
return;
}
int count = 1;
while (!node.isNull()) {
QDomElement profile = node.toElement();
QString profileName = profile.attribute(QStringLiteral("name"));
QString standard = profile.attribute(QStringLiteral("standard"));
QTextDocument docConvert;
docConvert.setHtml(profile.attribute(QStringLiteral("args")));
QString params = docConvert.toPlainText().simplified();
if (replaceVorbisCodec && params.contains(QStringLiteral("acodec=vorbis"))) {
// replace vorbis with libvorbis
params = params.replace(QLatin1String("=vorbis"), QLatin1String("=libvorbis"));
}
if (replaceLibfaacCodec && params.contains(QStringLiteral("acodec=aac"))) {
// replace libfaac with aac
params = params.replace(QLatin1String("aac"), QLatin1String("libfaac"));
}
QString prof_extension = profile.attribute(QStringLiteral("extension"));
if (!prof_extension.isEmpty()) {
extension = prof_extension;
}
QString groupName = profile.attribute(QStringLiteral("category"), i18nc("Category Name", "Custom"));
QList foundGroup = m_view.formats->findItems(groupName, Qt::MatchExactly);
QTreeWidgetItem *groupItem;
if (!foundGroup.isEmpty()) {
groupItem = foundGroup.takeFirst();
} else {
groupItem = new QTreeWidgetItem(QStringList(groupName));
if (editable) {
m_view.formats->insertTopLevelItem(0, groupItem);
} else {
m_view.formats->addTopLevelItem(groupItem);
groupItem->setExpanded(true);
}
}
// Check if item with same name already exists and replace it,
// allowing to override default profiles
QTreeWidgetItem *childitem = nullptr;
for (int j = 0; j < groupItem->childCount(); ++j) {
if (groupItem->child(j)->text(0) == profileName) {
childitem = groupItem->child(j);
break;
}
}
if (!childitem) {
childitem = new QTreeWidgetItem(QStringList(profileName));
}
childitem->setData(0, GroupRole, groupName);
childitem->setData(0, ExtensionRole, extension);
childitem->setData(0, RenderRole, "avformat");
childitem->setData(0, StandardRole, standard);
childitem->setData(0, ParamsRole, params);
if (params.contains(QLatin1String("%quality"))) {
childitem->setData(0, BitratesRole, profile.attribute(QStringLiteral("qualities")).split(QLatin1Char(','), QString::SkipEmptyParts));
} else if (params.contains(QLatin1String("%bitrate"))) {
childitem->setData(0, BitratesRole, profile.attribute(QStringLiteral("bitrates")).split(QLatin1Char(','), QString::SkipEmptyParts));
}
if (params.contains(QLatin1String("%audioquality"))) {
childitem->setData(0, AudioBitratesRole, profile.attribute(QStringLiteral("audioqualities")).split(QLatin1Char(','), QString::SkipEmptyParts));
} else if (params.contains(QLatin1String("%audiobitrate"))) {
childitem->setData(0, AudioBitratesRole, profile.attribute(QStringLiteral("audiobitrates")).split(QLatin1Char(','), QString::SkipEmptyParts));
}
if (profile.hasAttribute(QStringLiteral("speeds"))) {
childitem->setData(0, SpeedsRole, profile.attribute(QStringLiteral("speeds")).split(QLatin1Char(';'), QString::SkipEmptyParts));
}
if (profile.hasAttribute(QStringLiteral("url"))) {
childitem->setData(0, ExtraRole, profile.attribute(QStringLiteral("url")));
}
if (profile.hasAttribute(QStringLiteral("top_field_first"))) {
childitem->setData(0, FieldRole, profile.attribute(QStringLiteral("top_field_first")));
}
if (editable) {
childitem->setData(0, EditableRole, exportFile);
if (exportFile.endsWith(QLatin1String("customprofiles.xml"))) {
childitem->setIcon(0, QIcon::fromTheme(QStringLiteral("favorite")));
} else {
childitem->setIcon(0, QIcon::fromTheme(QStringLiteral("internet-services")));
}
}
groupItem->addChild(childitem);
node = doc.elementsByTagName(QStringLiteral("profile")).at(count);
count++;
}
return;
}
int i = 0;
QString groupName;
QString profileName;
QString prof_extension;
QString renderer;
QString params;
QString standard;
while (!groups.item(i).isNull()) {
documentElement = groups.item(i).toElement();
QDomNode gname = documentElement.elementsByTagName(QStringLiteral("groupname")).at(0);
groupName = documentElement.attribute(QStringLiteral("name"), i18nc("Attribute Name", "Custom"));
extension = documentElement.attribute(QStringLiteral("extension"), QString());
renderer = documentElement.attribute(QStringLiteral("renderer"), QString());
QList foundGroup = m_view.formats->findItems(groupName, Qt::MatchExactly);
QTreeWidgetItem *groupItem;
if (!foundGroup.isEmpty()) {
groupItem = foundGroup.takeFirst();
} else {
groupItem = new QTreeWidgetItem(QStringList(groupName));
m_view.formats->addTopLevelItem(groupItem);
groupItem->setExpanded(true);
}
QDomNode n = groups.item(i).firstChild();
while (!n.isNull()) {
if (n.toElement().tagName() != QLatin1String("profile")) {
n = n.nextSibling();
continue;
}
profileElement = n.toElement();
profileName = profileElement.attribute(QStringLiteral("name"));
standard = profileElement.attribute(QStringLiteral("standard"));
params = profileElement.attribute(QStringLiteral("args")).simplified();
if (replaceVorbisCodec && params.contains(QStringLiteral("acodec=vorbis"))) {
// replace vorbis with libvorbis
params = params.replace(QLatin1String("=vorbis"), QLatin1String("=libvorbis"));
}
if (replaceLibfaacCodec && params.contains(QStringLiteral("acodec=aac"))) {
// replace libfaac with aac
params = params.replace(QLatin1String("aac"), QLatin1String("libfaac"));
}
prof_extension = profileElement.attribute(QStringLiteral("extension"));
if (!prof_extension.isEmpty()) {
extension = prof_extension;
}
item = new QTreeWidgetItem(QStringList(profileName));
item->setData(0, GroupRole, groupName);
item->setData(0, ExtensionRole, extension);
item->setData(0, RenderRole, renderer);
item->setData(0, StandardRole, standard);
item->setData(0, ParamsRole, params);
if (params.contains(QLatin1String("%quality"))) {
item->setData(0, BitratesRole, profileElement.attribute(QStringLiteral("qualities")).split(QLatin1Char(','), QString::SkipEmptyParts));
} else if (params.contains(QLatin1String("%bitrate"))) {
item->setData(0, BitratesRole, profileElement.attribute(QStringLiteral("bitrates")).split(QLatin1Char(','), QString::SkipEmptyParts));
}
if (params.contains(QLatin1String("%audioquality"))) {
item->setData(0, AudioBitratesRole,
profileElement.attribute(QStringLiteral("audioqualities")).split(QLatin1Char(','), QString::SkipEmptyParts));
} else if (params.contains(QLatin1String("%audiobitrate"))) {
item->setData(0, AudioBitratesRole, profileElement.attribute(QStringLiteral("audiobitrates")).split(QLatin1Char(','), QString::SkipEmptyParts));
}
if (profileElement.hasAttribute(QStringLiteral("speeds"))) {
item->setData(0, SpeedsRole, profileElement.attribute(QStringLiteral("speeds")).split(QLatin1Char(';'), QString::SkipEmptyParts));
}
if (profileElement.hasAttribute(QStringLiteral("url"))) {
item->setData(0, ExtraRole, profileElement.attribute(QStringLiteral("url")));
}
groupItem->addChild(item);
n = n.nextSibling();
}
++i;
}
}
void RenderWidget::setRenderJob(const QString &dest, int progress)
{
RenderJobItem *item;
QList existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
if (!existing.isEmpty()) {
item = static_cast(existing.at(0));
} else {
item = new RenderJobItem(m_view.running_jobs, QStringList() << QString() << dest);
if (progress == 0) {
item->setStatus(WAITINGJOB);
}
}
item->setData(1, ProgressRole, progress);
item->setStatus(RUNNINGJOB);
if (progress == 0) {
item->setIcon(0, QIcon::fromTheme(QStringLiteral("media-record")));
item->setData(1, TimeRole, QDateTime::currentDateTime());
slotCheckJob();
} else {
QDateTime startTime = item->data(1, TimeRole).toDateTime();
qint64 elapsedTime = startTime.secsTo(QDateTime::currentDateTime());
qint64 remaining = elapsedTime * (100 - progress) / progress;
int days = static_cast(remaining / 86400);
int remainingSecs = static_cast(remaining % 86400);
QTime when = QTime(0, 0, 0, 0);
when = when.addSecs(remainingSecs);
QString est = (days > 0) ? i18np("%1 day ", "%1 days ", days) : QString();
est.append(when.toString(QStringLiteral("hh:mm:ss")));
QString t = i18n("Remaining time %1", est);
item->setData(1, Qt::UserRole, t);
}
}
void RenderWidget::setRenderStatus(const QString &dest, int status, const QString &error)
{
RenderJobItem *item;
QList existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
if (!existing.isEmpty()) {
item = static_cast(existing.at(0));
} else {
item = new RenderJobItem(m_view.running_jobs, QStringList() << QString() << dest);
}
if (!item) {
return;
}
if (status == -1) {
// Job finished successfully
item->setStatus(FINISHEDJOB);
QDateTime startTime = item->data(1, TimeRole).toDateTime();
qint64 elapsedTime = startTime.secsTo(QDateTime::currentDateTime());
int days = static_cast(elapsedTime / 86400);
int secs = static_cast(elapsedTime % 86400);
QTime when = QTime(0, 0, 0, 0);
when = when.addSecs(secs);
QString est = (days > 0) ? i18np("%1 day ", "%1 days ", days) : QString();
est.append(when.toString(QStringLiteral("hh:mm:ss")));
QString t = i18n("Rendering finished in %1", est);
item->setData(1, Qt::UserRole, t);
#ifdef KF5_USE_PURPOSE
m_shareMenu->model()->setInputData(QJsonObject{{QStringLiteral("mimeType"), QMimeDatabase().mimeTypeForFile(item->text(1)).name()},
{QStringLiteral("urls"), QJsonArray({item->text(1)})}});
m_shareMenu->model()->setPluginType(QStringLiteral("Export"));
m_shareMenu->reload();
#endif
QString notif = i18n("Rendering of %1 finished in %2", item->text(1), est);
KNotification *notify = new KNotification(QStringLiteral("RenderFinished"));
notify->setText(notif);
notify->setUrls({QUrl::fromLocalFile(dest)});
notify->sendEvent();
QString itemGroup = item->data(0, Qt::UserRole).toString();
if (itemGroup == QLatin1String("dvd")) {
emit openDvdWizard(item->text(1));
} else if (itemGroup == QLatin1String("websites")) {
QString url = item->metadata();
if (!url.isEmpty()) {
new KRun(QUrl::fromLocalFile(url), this);
}
}
} else if (status == -2) {
// Rendering crashed
item->setStatus(FAILEDJOB);
m_view.error_log->append(i18n("Rendering of %1 crashed ", dest));
m_view.error_log->append(error);
m_view.error_log->append(QStringLiteral(" "));
m_view.error_box->setVisible(true);
} else if (status == -3) {
// User aborted job
item->setStatus(ABORTEDJOB);
} else {
delete item;
}
slotCheckJob();
checkRenderStatus();
}
void RenderWidget::slotAbortCurrentJob()
{
auto *current = static_cast(m_view.running_jobs->currentItem());
if (current) {
if (current->status() == RUNNINGJOB) {
emit abortProcess(current->text(1));
} else {
delete current;
slotCheckJob();
checkRenderStatus();
}
}
}
void RenderWidget::slotStartCurrentJob()
{
auto *current = static_cast(m_view.running_jobs->currentItem());
if ((current != nullptr) && current->status() == WAITINGJOB) {
startRendering(current);
}
m_view.start_job->setEnabled(false);
}
void RenderWidget::slotCheckJob()
{
bool activate = false;
auto *current = static_cast(m_view.running_jobs->currentItem());
if (current) {
if (current->status() == RUNNINGJOB || current->status() == STARTINGJOB) {
m_view.abort_job->setText(i18n("Abort Job"));
m_view.start_job->setEnabled(false);
} else {
m_view.abort_job->setText(i18n("Remove Job"));
m_view.start_job->setEnabled(current->status() == WAITINGJOB);
}
activate = true;
#ifdef KF5_USE_PURPOSE
if (current->status() == FINISHEDJOB) {
m_shareMenu->model()->setInputData(QJsonObject{{QStringLiteral("mimeType"), QMimeDatabase().mimeTypeForFile(current->text(1)).name()},
{QStringLiteral("urls"), QJsonArray({current->text(1)})}});
m_shareMenu->model()->setPluginType(QStringLiteral("Export"));
m_shareMenu->reload();
m_view.shareButton->setEnabled(true);
} else {
m_view.shareButton->setEnabled(false);
}
#endif
}
m_view.abort_job->setEnabled(activate);
/*
for (int i = 0; i < m_view.running_jobs->topLevelItemCount(); ++i) {
current = static_cast(m_view.running_jobs->topLevelItem(i));
if (current == static_cast (m_view.running_jobs->currentItem())) {
current->setSizeHint(1, QSize(m_view.running_jobs->columnWidth(1), fontMetrics().height() * 3));
} else current->setSizeHint(1, QSize(m_view.running_jobs->columnWidth(1), fontMetrics().height() * 2));
}*/
}
void RenderWidget::slotCLeanUpJobs()
{
int ix = 0;
auto *current = static_cast(m_view.running_jobs->topLevelItem(ix));
while (current != nullptr) {
if (current->status() == FINISHEDJOB || current->status() == ABORTEDJOB) {
delete current;
} else {
ix++;
}
current = static_cast(m_view.running_jobs->topLevelItem(ix));
}
slotCheckJob();
}
void RenderWidget::parseScriptFiles()
{
QStringList scriptsFilter;
scriptsFilter << QStringLiteral("*.mlt");
m_view.scripts_list->clear();
QTreeWidgetItem *item;
// List the project scripts
QDir projectFolder(pCore->currentDoc()->projectDataFolder());
projectFolder.mkpath(QStringLiteral("kdenlive-renderqueue"));
projectFolder.cd(QStringLiteral("kdenlive-renderqueue"));
QStringList scriptFiles = projectFolder.entryList(scriptsFilter, QDir::Files);
for (int i = 0; i < scriptFiles.size(); ++i) {
QUrl scriptpath = QUrl::fromLocalFile(projectFolder.absoluteFilePath(scriptFiles.at(i)));
QFile f(scriptpath.toLocalFile());
QDomDocument doc;
doc.setContent(&f, false);
f.close();
QDomElement consumer = doc.documentElement().firstChildElement(QStringLiteral("consumer"));
if (consumer.isNull()) {
continue;
}
QString target = consumer.attribute(QStringLiteral("target"));
if (target.isEmpty()) {
continue;
}
item = new QTreeWidgetItem(m_view.scripts_list, QStringList() << QString() << scriptpath.fileName());
auto icon = QFileIconProvider().icon(QFileInfo(f));
item->setIcon(0, icon.isNull() ? QIcon::fromTheme(QStringLiteral("application-x-executable-script")) : icon);
item->setSizeHint(0, QSize(m_view.scripts_list->columnWidth(0), fontMetrics().height() * 2));
item->setData(1, Qt::UserRole, QUrl(QUrl::fromEncoded(target.toUtf8())).url(QUrl::PreferLocalFile));
item->setData(1, Qt::UserRole + 1, scriptpath.toLocalFile());
}
QTreeWidgetItem *script = m_view.scripts_list->topLevelItem(0);
if (script) {
m_view.scripts_list->setCurrentItem(script);
script->setSelected(true);
}
}
void RenderWidget::slotCheckScript()
{
QTreeWidgetItem *current = m_view.scripts_list->currentItem();
if (current == nullptr) {
return;
}
m_view.start_script->setEnabled(current->data(0, Qt::UserRole).toString().isEmpty());
m_view.delete_script->setEnabled(true);
for (int i = 0; i < m_view.scripts_list->topLevelItemCount(); ++i) {
current = m_view.scripts_list->topLevelItem(i);
if (current == m_view.scripts_list->currentItem()) {
current->setSizeHint(1, QSize(m_view.scripts_list->columnWidth(1), fontMetrics().height() * 3));
} else {
current->setSizeHint(1, QSize(m_view.scripts_list->columnWidth(1), fontMetrics().height() * 2));
}
}
}
void RenderWidget::slotStartScript()
{
auto *item = static_cast(m_view.scripts_list->currentItem());
if (item) {
QString destination = item->data(1, Qt::UserRole).toString();
if (QFile::exists(destination)) {
if (KMessageBox::warningYesNo(this, i18n("Output file already exists. Do you want to overwrite it?")) != KMessageBox::Yes) {
return;
}
}
QString path = item->data(1, Qt::UserRole + 1).toString();
// Insert new job in queue
RenderJobItem *renderItem = nullptr;
QList existing = m_view.running_jobs->findItems(destination, Qt::MatchExactly, 1);
if (!existing.isEmpty()) {
renderItem = static_cast(existing.at(0));
if (renderItem->status() == RUNNINGJOB || renderItem->status() == WAITINGJOB || renderItem->status() == STARTINGJOB) {
KMessageBox::information(
this, i18n("There is already a job writing file:%1 Abort the job if you want to overwrite it...", destination),
i18n("Already running"));
return;
}
delete renderItem;
renderItem = nullptr;
}
if (!renderItem) {
renderItem = new RenderJobItem(m_view.running_jobs, QStringList() << QString() << destination);
}
renderItem->setData(1, ProgressRole, 0);
renderItem->setStatus(WAITINGJOB);
renderItem->setIcon(0, QIcon::fromTheme(QStringLiteral("media-playback-pause")));
renderItem->setData(1, Qt::UserRole, i18n("Waiting..."));
renderItem->setData(1, TimeRole, QDateTime::currentDateTime());
QStringList argsJob = {KdenliveSettings::rendererpath(), path, destination, QStringLiteral("-pid:%1").arg(QCoreApplication::applicationPid())};
renderItem->setData(1, ParametersRole, argsJob);
checkRenderStatus();
m_view.tabWidget->setCurrentIndex(1);
}
}
void RenderWidget::slotDeleteScript()
{
QTreeWidgetItem *item = m_view.scripts_list->currentItem();
if (item) {
QString path = item->data(1, Qt::UserRole + 1).toString();
bool success = true;
success &= static_cast(QFile::remove(path));
if (!success) {
qCWarning(KDENLIVE_LOG) << "// Error removing script or playlist: " << path << ", " << path << ".mlt";
}
parseScriptFiles();
}
}
void RenderWidget::slotGenerateScript()
{
slotPrepareExport(true);
}
void RenderWidget::slotHideLog()
{
m_view.error_box->setVisible(false);
}
void RenderWidget::setRenderProfile(const QMap &props)
{
m_view.scanning_list->setCurrentIndex(props.value(QStringLiteral("renderscanning")).toInt());
m_view.field_order->setCurrentIndex(props.value(QStringLiteral("renderfield")).toInt());
int exportAudio = props.value(QStringLiteral("renderexportaudio")).toInt();
switch (exportAudio) {
case 1:
m_view.export_audio->setCheckState(Qt::Unchecked);
break;
case 2:
m_view.export_audio->setCheckState(Qt::Checked);
break;
default:
m_view.export_audio->setCheckState(Qt::PartiallyChecked);
}
if (props.contains(QStringLiteral("renderrescale"))) {
m_view.rescale->setChecked(props.value(QStringLiteral("renderrescale")).toInt() != 0);
}
if (props.contains(QStringLiteral("renderrescalewidth"))) {
m_view.rescale_width->setValue(props.value(QStringLiteral("renderrescalewidth")).toInt());
} else {
std::unique_ptr &profile = pCore->getCurrentProfile();
m_view.rescale_width->setValue(profile->width() / 2);
slotUpdateRescaleWidth(m_view.rescale_width->value());
}
if (props.contains(QStringLiteral("renderrescaleheight"))) {
m_view.rescale_height->setValue(props.value(QStringLiteral("renderrescaleheight")).toInt());
}
if (props.contains(QStringLiteral("rendertcoverlay"))) {
m_view.tc_overlay->setChecked(props.value(QStringLiteral("rendertcoverlay")).toInt() != 0);
}
if (props.contains(QStringLiteral("rendertctype"))) {
m_view.tc_type->setCurrentIndex(props.value(QStringLiteral("rendertctype")).toInt());
}
if (props.contains(QStringLiteral("renderratio"))) {
m_view.rescale_keep->setChecked(props.value(QStringLiteral("renderratio")).toInt() != 0);
}
if (props.contains(QStringLiteral("renderplay"))) {
m_view.play_after->setChecked(props.value(QStringLiteral("renderplay")).toInt() != 0);
}
if (props.contains(QStringLiteral("rendertwopass"))) {
m_view.checkTwoPass->setChecked(props.value(QStringLiteral("rendertwopass")).toInt() != 0);
}
if (props.value(QStringLiteral("renderzone")) == QLatin1String("1")) {
m_view.render_zone->setChecked(true);
} else if (props.value(QStringLiteral("renderguide")) == QLatin1String("1")) {
m_view.render_guide->setChecked(true);
m_view.guide_start->setCurrentIndex(props.value(QStringLiteral("renderstartguide")).toInt());
m_view.guide_end->setCurrentIndex(props.value(QStringLiteral("renderendguide")).toInt());
} else {
m_view.render_full->setChecked(true);
}
slotUpdateGuideBox();
QString url = props.value(QStringLiteral("renderurl"));
if (url.isEmpty()) {
QTreeWidgetItem *item = m_view.formats->currentItem();
if (item && item->parent()) { // categories have no parent
const QString extension = item->data(0, ExtensionRole).toString();
url = filenameWithExtension(QUrl::fromLocalFile(pCore->currentDoc()->projectDataFolder() + QDir::separator()), extension).toLocalFile();
}
} else if (QFileInfo(url).isRelative()) {
url.prepend(pCore->currentDoc()->documentRoot());
}
m_view.out_file->setUrl(QUrl::fromLocalFile(url));
if (props.contains(QStringLiteral("renderprofile")) || props.contains(QStringLiteral("rendercategory"))) {
focusFirstVisibleItem(props.value(QStringLiteral("renderprofile")));
}
if (props.contains(QStringLiteral("renderquality"))) {
m_view.video->setValue(props.value(QStringLiteral("renderquality")).toInt());
} else if (props.contains(QStringLiteral("renderbitrate"))) {
m_view.video->setValue(props.value(QStringLiteral("renderbitrate")).toInt());
} else {
m_view.quality->setValue(m_view.quality->maximum() * 3 / 4);
}
if (props.contains(QStringLiteral("renderaudioquality"))) {
m_view.audio->setValue(props.value(QStringLiteral("renderaudioquality")).toInt());
}
if (props.contains(QStringLiteral("renderaudiobitrate"))) {
m_view.audio->setValue(props.value(QStringLiteral("renderaudiobitrate")).toInt());
}
if (props.contains(QStringLiteral("renderspeed"))) {
m_view.speed->setValue(props.value(QStringLiteral("renderspeed")).toInt());
}
}
bool RenderWidget::startWaitingRenderJobs()
{
m_blockProcessing = true;
#ifdef Q_OS_WIN
const QLatin1String ScriptFormat(".bat");
#else
const QLatin1String ScriptFormat(".sh");
#endif
QTemporaryFile tmp(QDir::tempPath() + QStringLiteral("/kdenlive-XXXXXX") + ScriptFormat);
if (!tmp.open()) {
// Something went wrong
return false;
}
tmp.close();
QString autoscriptFile = tmp.fileName();
QFile file(autoscriptFile);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qCWarning(KDENLIVE_LOG) << "////// ERROR writing to file: " << autoscriptFile;
KMessageBox::error(nullptr, i18n("Cannot write to file %1", autoscriptFile));
return false;
}
QTextStream outStream(&file);
#ifndef Q_OS_WIN
outStream << "#! /bin/sh" << '\n' << '\n';
#endif
auto *item = static_cast(m_view.running_jobs->topLevelItem(0));
while (item != nullptr) {
if (item->status() == WAITINGJOB) {
// Add render process for item
const QString params = item->data(1, ParametersRole).toStringList().join(QLatin1Char(' '));
outStream << '\"' << m_renderer << "\" " << params << '\n';
}
item = static_cast(m_view.running_jobs->itemBelow(item));
}
// erase itself when rendering is finished
#ifndef Q_OS_WIN
outStream << "rm \"" << autoscriptFile << "\"\n";
#else
outStream << "del \"" << autoscriptFile << "\"\n";
#endif
if (file.error() != QFile::NoError) {
KMessageBox::error(nullptr, i18n("Cannot write to file %1", autoscriptFile));
file.close();
m_blockProcessing = false;
return false;
}
file.close();
QFile::setPermissions(autoscriptFile, file.permissions() | QFile::ExeUser);
QProcess::startDetached(autoscriptFile, QStringList());
return true;
}
void RenderWidget::slotPlayRendering(QTreeWidgetItem *item, int)
{
auto *renderItem = static_cast(item);
if (renderItem->status() != FINISHEDJOB) {
return;
}
new KRun(QUrl::fromLocalFile(item->text(1)), this);
}
void RenderWidget::errorMessage(RenderError type, const QString &message)
{
QString fullMessage;
m_errorMessages.insert(type, message);
QMapIterator i(m_errorMessages);
while (i.hasNext()) {
i.next();
if (!i.value().isEmpty()) {
if (!fullMessage.isEmpty()) {
fullMessage.append(QLatin1Char('\n'));
}
fullMessage.append(i.value());
}
}
if (!fullMessage.isEmpty()) {
m_infoMessage->setMessageType(KMessageWidget::Warning);
m_infoMessage->setText(fullMessage);
m_infoMessage->show();
} else {
m_infoMessage->hide();
}
}
void RenderWidget::slotUpdateEncodeThreads(int val)
{
KdenliveSettings::setEncodethreads(val);
}
void RenderWidget::slotUpdateRescaleWidth(int val)
{
KdenliveSettings::setDefaultrescalewidth(val);
if (!m_view.rescale_keep->isChecked()) {
return;
}
m_view.rescale_height->blockSignals(true);
std::unique_ptr &profile = pCore->getCurrentProfile();
m_view.rescale_height->setValue(val * profile->height() / profile->width());
KdenliveSettings::setDefaultrescaleheight(m_view.rescale_height->value());
m_view.rescale_height->blockSignals(false);
}
void RenderWidget::slotUpdateRescaleHeight(int val)
{
KdenliveSettings::setDefaultrescaleheight(val);
if (!m_view.rescale_keep->isChecked()) {
return;
}
m_view.rescale_width->blockSignals(true);
std::unique_ptr &profile = pCore->getCurrentProfile();
m_view.rescale_width->setValue(val * profile->width() / profile->height());
KdenliveSettings::setDefaultrescaleheight(m_view.rescale_width->value());
m_view.rescale_width->blockSignals(false);
}
void RenderWidget::slotSwitchAspectRatio()
{
KdenliveSettings::setRescalekeepratio(m_view.rescale_keep->isChecked());
if (m_view.rescale_keep->isChecked()) {
slotUpdateRescaleWidth(m_view.rescale_width->value());
}
}
void RenderWidget::slotUpdateAudioLabel(int ix)
{
if (ix == Qt::PartiallyChecked) {
m_view.export_audio->setText(i18n("Export audio (automatic)"));
} else {
m_view.export_audio->setText(i18n("Export audio"));
}
m_view.stemAudioExport->setEnabled(ix != Qt::Unchecked);
}
bool RenderWidget::automaticAudioExport() const
{
return (m_view.export_audio->checkState() == Qt::PartiallyChecked);
}
bool RenderWidget::selectedAudioExport() const
{
return (m_view.export_audio->checkState() != Qt::Unchecked);
}
void RenderWidget::updateProxyConfig(bool enable)
{
m_view.proxy_render->setHidden(!enable);
}
bool RenderWidget::proxyRendering()
{
return m_view.proxy_render->isChecked();
}
bool RenderWidget::isStemAudioExportEnabled() const
{
return (m_view.stemAudioExport->isChecked() && m_view.stemAudioExport->isVisible() && m_view.stemAudioExport->isEnabled());
}
void RenderWidget::setRescaleEnabled(bool enable)
{
for (int i = 0; i < m_view.rescale_box->layout()->count(); ++i) {
if (m_view.rescale_box->itemAt(i)->widget()) {
m_view.rescale_box->itemAt(i)->widget()->setEnabled(enable);
}
}
}
void RenderWidget::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
switch (m_view.tabWidget->currentIndex()) {
case 1:
if (m_view.start_job->isEnabled()) {
slotStartCurrentJob();
}
break;
case 2:
if (m_view.start_script->isEnabled()) {
slotStartScript();
}
break;
default:
if (m_view.buttonRender->isEnabled()) {
slotPrepareExport();
}
break;
}
} else if (e->key() == Qt::Key_Delete) {
// If in Scripts tab, let Del key invoke DeleteScript
if (m_view.tabWidget->currentIndex() == 2) {
if (m_view.delete_script->isEnabled()) {
slotDeleteScript();
}
} else {
QDialog::keyPressEvent(e);
}
} else {
QDialog::keyPressEvent(e);
}
}
void RenderWidget::adjustAVQualities(int quality)
{
// calculate video/audio quality indexes from the general quality cursor
// taking into account decreasing/increasing video/audio quality parameter
double q = double(quality) / m_view.quality->maximum();
int dq = int(q * (m_view.video->maximum() - m_view.video->minimum()));
// prevent video spinbox to update quality cursor (loop)
m_view.video->blockSignals(true);
m_view.video->setValue(m_view.video->property("decreasing").toBool() ? m_view.video->maximum() - dq : m_view.video->minimum() + dq);
m_view.video->blockSignals(false);
dq = int(q * (m_view.audio->maximum() - m_view.audio->minimum()));
dq -= dq % m_view.audio->singleStep(); // keep a 32 pitch for bitrates
m_view.audio->setValue(m_view.audio->property("decreasing").toBool() ? m_view.audio->maximum() - dq : m_view.audio->minimum() + dq);
}
void RenderWidget::adjustQuality(int videoQuality)
{
int dq = videoQuality * m_view.quality->maximum() / (m_view.video->maximum() - m_view.video->minimum());
m_view.quality->blockSignals(true);
m_view.quality->setValue(m_view.video->property("decreasing").toBool() ? m_view.video->maximum() - dq : m_view.video->minimum() + dq);
m_view.quality->blockSignals(false);
}
void RenderWidget::adjustSpeed(int speedIndex)
{
if (m_view.formats->currentItem()) {
QStringList speeds = m_view.formats->currentItem()->data(0, SpeedsRole).toStringList();
if (speedIndex < speeds.count()) {
m_view.speed->setToolTip(i18n("Codec speed parameters:\n%1", speeds.at(speedIndex)));
}
}
}
void RenderWidget::checkCodecs()
{
Mlt::Profile p;
auto *consumer = new Mlt::Consumer(p, "avformat");
if (consumer) {
consumer->set("vcodec", "list");
consumer->set("acodec", "list");
consumer->set("f", "list");
consumer->start();
vcodecsList.clear();
Mlt::Properties vcodecs(mlt_properties(consumer->get_data("vcodec")));
vcodecsList.reserve(vcodecs.count());
for (int i = 0; i < vcodecs.count(); ++i) {
vcodecsList << QString(vcodecs.get(i));
}
acodecsList.clear();
Mlt::Properties acodecs(mlt_properties(consumer->get_data("acodec")));
acodecsList.reserve(acodecs.count());
for (int i = 0; i < acodecs.count(); ++i) {
acodecsList << QString(acodecs.get(i));
}
supportedFormats.clear();
Mlt::Properties formats(mlt_properties(consumer->get_data("f")));
supportedFormats.reserve(formats.count());
for (int i = 0; i < formats.count(); ++i) {
supportedFormats << QString(formats.get(i));
}
delete consumer;
}
}
void RenderWidget::slotProxyWarn(bool enableProxy)
{
errorMessage(ProxyWarning, enableProxy ? i18n("Rendering using low quality proxy") : QString());
}
void RenderWidget::prepareMenu(const QPoint &pos)
{
QTreeWidgetItem *nd = m_view.running_jobs->itemAt( pos );
RenderJobItem *renderItem = nullptr;
if (nd) {
renderItem = static_cast(nd);
}
if (!renderItem) {
return;
}
if (renderItem->status() != FINISHEDJOB) {
return;
}
QMenu menu(this);
QAction *newAct = new QAction(i18n("Add to current project"), this);
connect(newAct, &QAction::triggered, [&, renderItem]() {
pCore->bin()->slotAddClipToProject(QUrl::fromLocalFile(renderItem->text(1)));
});
menu.addAction(newAct);
menu.exec(m_view.running_jobs->mapToGlobal(pos));
}
diff --git a/src/dialogs/titletemplatedialog.cpp b/src/dialogs/titletemplatedialog.cpp
index 982227c9d..62ab5b228 100644
--- a/src/dialogs/titletemplatedialog.cpp
+++ b/src/dialogs/titletemplatedialog.cpp
@@ -1,104 +1,100 @@
/*
Copyright (C) 2016 Jean-Baptiste Mardelle
This file is part of Kdenlive. See www.kdenlive.org.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License or (at your option) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "titletemplatedialog.h"
#include "doc/kthumb.h"
#include "kdenlivesettings.h"
#include "klocalizedstring.h"
#include
#include
#include
TitleTemplateDialog::TitleTemplateDialog(const QString &folder, QWidget *parent)
: QDialog(parent)
{
m_view.setupUi(this);
// Get the list of existing templates
const QStringList filter = {QStringLiteral("*.kdenlivetitle")};
const QString path = folder + QStringLiteral("/titles/");
// Project templates
QDir dir(path);
const QStringList templateFiles = dir.entryList(filter, QDir::Files);
for (const QString &fname : templateFiles) {
m_view.template_list->comboBox()->addItem(fname, dir.absoluteFilePath(fname));
}
// System templates
const QStringList titleTemplates = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("titles/"), QStandardPaths::LocateDirectory);
for (const QString &folderpath : titleTemplates) {
QDir sysdir(folderpath);
const QStringList filesnames = sysdir.entryList(filter, QDir::Files);
for (const QString &fname : filesnames) {
m_view.template_list->comboBox()->addItem(fname, sysdir.absoluteFilePath(fname));
}
}
if (m_view.template_list->comboBox()->count() > 0) {
m_view.buttonBox->button(QDialogButtonBox::Ok)->setFocus();
}
int current = m_view.template_list->comboBox()->findText(KdenliveSettings::selected_template());
if (current > -1) {
m_view.template_list->comboBox()->setCurrentIndex(current);
}
const QStringList mimeTypeFilters = {QStringLiteral("application/x-kdenlivetitle")};
m_view.template_list->setMimeTypeFilters(mimeTypeFilters);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_view.template_list->comboBox(), static_cast(&KComboBox::currentIndexChanged), this,
-#else
- connect(m_view.template_list->comboBox(), static_cast(&KComboBox::currentIndexChanged) , this,
-#endif
&TitleTemplateDialog::updatePreview);
updatePreview();
}
QString TitleTemplateDialog::selectedTemplate() const
{
QString textTemplate = m_view.template_list->comboBox()->itemData(m_view.template_list->comboBox()->currentIndex()).toString();
if (textTemplate.isEmpty()) {
textTemplate = m_view.template_list->comboBox()->currentText();
}
return textTemplate;
}
QString TitleTemplateDialog::selectedText() const
{
return m_view.description->toPlainText();
}
void TitleTemplateDialog::resizeEvent(QResizeEvent *event)
{
updatePreview();
QWidget::resizeEvent(event);
}
void TitleTemplateDialog::updatePreview()
{
QString textTemplate = m_view.template_list->comboBox()->itemData(m_view.template_list->comboBox()->currentIndex()).toString();
if (textTemplate.isEmpty()) {
textTemplate = m_view.template_list->comboBox()->currentText();
}
QPixmap pix = KThumb::getImage(QUrl::fromLocalFile(textTemplate), m_view.preview->width());
m_view.preview->setPixmap(pix);
KdenliveSettings::setSelected_template(m_view.template_list->comboBox()->currentText());
}
diff --git a/src/dvdwizard/dvdwizardchapters.cpp b/src/dvdwizard/dvdwizardchapters.cpp
index cef5ea3ae..d355acbce 100644
--- a/src/dvdwizard/dvdwizardchapters.cpp
+++ b/src/dvdwizard/dvdwizardchapters.cpp
@@ -1,276 +1,272 @@
/***************************************************************************
* Copyright (C) 2009 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "dvdwizardchapters.h"
DvdWizardChapters::DvdWizardChapters(MonitorManager *manager, DVDFORMAT format, QWidget *parent)
: QWizardPage(parent)
, m_format(format)
, m_monitor(nullptr)
, m_manager(manager)
{
m_view.setupUi(this);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_view.vob_list, static_cast(&KComboBox::currentIndexChanged), this, &DvdWizardChapters::slotUpdateChaptersList);
-#else
- connect(m_view.vob_list, static_cast(&KComboBox::currentIndexChanged) , this, &DvdWizardChapters::slotUpdateChaptersList);
-#endif
connect(m_view.button_add, &QAbstractButton::clicked, this, &DvdWizardChapters::slotAddChapter);
connect(m_view.button_delete, &QAbstractButton::clicked, this, &DvdWizardChapters::slotRemoveChapter);
connect(m_view.chapters_list, &QListWidget::itemSelectionChanged, this, &DvdWizardChapters::slotGoToChapter);
connect(m_view.chapters_box, &QCheckBox::stateChanged, this, &DvdWizardChapters::slotEnableChapters);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
// Build monitor for chapters
auto *vbox = new QVBoxLayout;
m_view.video_frame->setLayout(vbox);
if (m_format == PAL || m_format == PAL_WIDE) {
m_tc.setFormat(25);
} else {
m_tc.setFormat(30000.0 / 1001);
}
}
DvdWizardChapters::~DvdWizardChapters()
{
if (m_monitor) {
m_monitor->stop();
m_manager->removeMonitor(m_monitor);
delete m_monitor;
}
}
void DvdWizardChapters::stopMonitor()
{
if (m_monitor) {
m_monitor->pause();
}
}
void DvdWizardChapters::slotUpdateChaptersList()
{
m_monitor->show();
m_manager->activateMonitor(Kdenlive::DvdMonitor);
m_monitor->start();
m_monitor->slotOpenDvdFile(m_view.vob_list->currentText());
m_monitor->adjustRulerSize(m_view.vob_list->itemData(m_view.vob_list->currentIndex(), Qt::UserRole).toInt());
QStringList currentChaps = m_view.vob_list->itemData(m_view.vob_list->currentIndex(), Qt::UserRole + 1).toStringList();
// insert chapters
QStringList chaptersString;
for (int i = 0; i < currentChaps.count(); ++i) {
chaptersString.append(Timecode::getStringTimecode(currentChaps.at(i).toInt(), m_tc.fps(), true));
}
m_view.chapters_list->clear();
m_view.chapters_list->addItems(chaptersString);
updateMonitorMarkers();
m_monitor->refreshMonitorIfActive();
// bool modified = m_view.vob_list->itemData(m_view.vob_list->currentIndex(), Qt::UserRole + 2).toInt();
}
void DvdWizardChapters::slotAddChapter()
{
int pos = m_monitor->position();
QStringList currentChaps = m_view.vob_list->itemData(m_view.vob_list->currentIndex(), Qt::UserRole + 1).toStringList();
if (currentChaps.contains(QString::number(pos))) {
return;
}
currentChaps.append(QString::number(pos));
QList chapterTimes;
chapterTimes.reserve(currentChaps.count());
for (int i = 0; i < currentChaps.count(); ++i) {
chapterTimes.append(currentChaps.at(i).toInt());
}
std::sort(chapterTimes.begin(), chapterTimes.end());
// rebuild chapters
QStringList chaptersString;
currentChaps.clear();
for (int i = 0; i < chapterTimes.count(); ++i) {
chaptersString.append(Timecode::getStringTimecode(chapterTimes.at(i), m_tc.fps(), true));
currentChaps.append(QString::number(chapterTimes.at(i)));
}
// Save item chapters
m_view.vob_list->setItemData(m_view.vob_list->currentIndex(), currentChaps, Qt::UserRole + 1);
// Mark item as modified
m_view.vob_list->setItemData(m_view.vob_list->currentIndex(), 1, Qt::UserRole + 2);
m_view.chapters_list->clear();
m_view.chapters_list->addItems(chaptersString);
updateMonitorMarkers();
}
void DvdWizardChapters::updateMonitorMarkers()
{
const QStringList chapters = m_view.vob_list->itemData(m_view.vob_list->currentIndex(), Qt::UserRole + 1).toStringList();
QList markers;
markers.reserve(chapters.count());
for (const QString &frame : chapters) {
markers << CommentedTime(GenTime(frame.toInt(), m_tc.fps()), QString());
}
// TODO: reimplement makers as model
// m_monitor->setMarkers(markers);
}
void DvdWizardChapters::slotRemoveChapter()
{
int ix = m_view.chapters_list->currentRow();
QStringList currentChaps = m_view.vob_list->itemData(m_view.vob_list->currentIndex(), Qt::UserRole + 1).toStringList();
currentChaps.removeAt(ix);
// Save item chapters
m_view.vob_list->setItemData(m_view.vob_list->currentIndex(), currentChaps, Qt::UserRole + 1);
// Mark item as modified
m_view.vob_list->setItemData(m_view.vob_list->currentIndex(), 1, Qt::UserRole + 2);
// rebuild chapters
QStringList chaptersString;
for (int i = 0; i < currentChaps.count(); ++i) {
chaptersString.append(Timecode::getStringTimecode(currentChaps.at(i).toInt(), m_tc.fps(), true));
}
m_view.chapters_list->clear();
m_view.chapters_list->addItems(chaptersString);
updateMonitorMarkers();
}
void DvdWizardChapters::slotGoToChapter()
{
if (m_view.chapters_list->currentItem()) {
m_monitor->setTimePos(m_tc.reformatSeparators(m_view.chapters_list->currentItem()->text()));
}
}
void DvdWizardChapters::createMonitor(DVDFORMAT format)
{
if (m_monitor == nullptr) {
// TODO: allow monitor with different profile for DVD
QString profile = DvdWizardVob::getDvdProfile(format);
m_monitor = new Monitor(Kdenlive::DvdMonitor, m_manager, this);
m_monitor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_view.video_frame->layout()->addWidget(m_monitor);
m_manager->appendMonitor(m_monitor);
m_monitor->setCustomProfile(profile, m_tc);
m_manager->activateMonitor(Kdenlive::DvdMonitor);
m_monitor->start();
}
}
void DvdWizardChapters::setVobFiles(DVDFORMAT format, const QStringList &movies, const QStringList &durations, const QStringList &chapters)
{
m_format = format;
if (m_format == PAL || m_format == PAL_WIDE) {
m_tc.setFormat(25);
} else {
m_tc.setFormat(30000.0 / 1001);
}
m_view.vob_list->blockSignals(true);
m_view.vob_list->clear();
for (int i = 0; i < movies.count(); ++i) {
m_view.vob_list->addItem(movies.at(i), durations.at(i));
m_view.vob_list->setItemData(i, chapters.at(i).split(QLatin1Char(';')), Qt::UserRole + 1);
}
m_view.vob_list->blockSignals(false);
if (m_view.chapters_box->checkState() == Qt::Checked) {
slotUpdateChaptersList();
}
}
QMap DvdWizardChapters::chaptersData() const
{
QMap result;
int max = m_view.vob_list->count();
for (int i = 0; i < max; ++i) {
QString chapters = m_view.vob_list->itemData(i, Qt::UserRole + 1).toStringList().join(QLatin1Char(';'));
result.insert(m_view.vob_list->itemText(i), chapters);
}
return result;
}
QStringList DvdWizardChapters::selectedTitles() const
{
QStringList result;
int max = m_view.vob_list->count();
for (int i = 0; i < max; ++i) {
result.append(m_view.vob_list->itemText(i));
if (!m_view.chapters_box->isChecked()) {
continue;
}
QStringList chapters = m_view.vob_list->itemData(i, Qt::UserRole + 1).toStringList();
for (int j = 0; j < chapters.count(); ++j) {
result.append(Timecode::getStringTimecode(chapters.at(j).toInt(), m_tc.fps(), true));
}
}
return result;
}
QStringList DvdWizardChapters::chapters(int ix) const
{
QStringList result;
QStringList chapters = m_view.vob_list->itemData(ix, Qt::UserRole + 1).toStringList();
for (int j = 0; j < chapters.count(); ++j) {
result.append(Timecode::getStringTimecode(chapters.at(j).toInt(), m_tc.fps(), true));
}
return result;
}
QStringList DvdWizardChapters::selectedTargets() const
{
QStringList result;
int max = m_view.vob_list->count();
for (int i = 0; i < max; ++i) {
// rightJustified: fill with 0s to make menus with more than 9 buttons work (now up to 99 buttons possible)
result.append(QStringLiteral("jump title ") + QString::number(i + 1).rightJustified(2, '0'));
if (!m_view.chapters_box->isChecked()) {
continue;
}
QStringList chapters = m_view.vob_list->itemData(i, Qt::UserRole + 1).toStringList();
for (int j = 0; j < chapters.count(); ++j) {
result.append(QStringLiteral("jump title ") + QString::number(i + 1).rightJustified(2, '0') + QStringLiteral(" chapter ") +
QString::number(j + 1).rightJustified(2, '0'));
}
}
return result;
}
QDomElement DvdWizardChapters::toXml() const
{
QDomDocument doc;
QDomElement xml = doc.createElement(QStringLiteral("xml"));
doc.appendChild(xml);
if (!m_view.chapters_box->isChecked()) {
return doc.documentElement();
}
for (int i = 0; i < m_view.vob_list->count(); ++i) {
QDomElement vob = doc.createElement(QStringLiteral("vob"));
vob.setAttribute(QStringLiteral("file"), m_view.vob_list->itemText(i));
vob.setAttribute(QStringLiteral("chapters"), m_view.vob_list->itemData(i, Qt::UserRole + 1).toStringList().join(QLatin1Char(';')));
xml.appendChild(vob);
}
return doc.documentElement();
}
void DvdWizardChapters::slotEnableChapters(int state)
{
m_view.chapters_frame->setEnabled(state == Qt::Checked);
if (state == Qt::Checked) {
createMonitor(m_format);
slotUpdateChaptersList();
}
}
diff --git a/src/dvdwizard/dvdwizardmenu.cpp b/src/dvdwizard/dvdwizardmenu.cpp
index 4557c9895..ce8ee61e9 100644
--- a/src/dvdwizard/dvdwizardmenu.cpp
+++ b/src/dvdwizard/dvdwizardmenu.cpp
@@ -1,916 +1,912 @@
/***************************************************************************
* Copyright (C) 2009 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "dvdwizardmenu.h"
#include "kdenlivesettings.h"
#include "doc/kthumb.h"
#include "kdenlive_debug.h"
#include "klocalizedstring.h"
#include
#include
#include
#include
enum { DvdButtonItem = QGraphicsItem::UserType + 1, DvdButtonUnderlineItem = QGraphicsItem::UserType + 2 };
DvdScene::DvdScene(QObject *parent)
: QGraphicsScene(parent)
{
}
void DvdScene::setProfile(int width, int height)
{
m_width = width;
m_height = height;
setSceneRect(0, 0, m_width, m_height);
}
int DvdScene::gridSize() const
{
return m_gridSize;
}
void DvdScene::setGridSize(int gridSize)
{
m_gridSize = gridSize;
}
void DvdScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
QGraphicsScene::mouseReleaseEvent(mouseEvent);
emit sceneChanged();
}
void DvdScene::drawForeground(QPainter *painter, const QRectF &rect)
{
// draw the grid if needed
if (gridSize() <= 1) {
return;
}
QPen pen(QColor(255, 0, 0, 100));
painter->setPen(pen);
qreal left = int(rect.left()) - (int(rect.left()) % m_gridSize);
qreal top = int(rect.top()) - (int(rect.top()) % m_gridSize);
QVector points;
for (qreal x = left; x < rect.right(); x += m_gridSize) {
for (qreal y = top; y < rect.bottom(); y += m_gridSize) {
points.append(QPointF(x, y));
}
}
painter->drawPoints(points.data(), points.size());
}
DvdButton::DvdButton(const QString &text)
: QGraphicsTextItem(text)
, m_target(0)
, m_command(QStringLiteral("jump title 1"))
, m_backToMenu(false)
{
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
}
void DvdButton::setTarget(int t, const QString &c)
{
m_target = t;
m_command = c;
}
int DvdButton::target() const
{
return m_target;
}
QString DvdButton::command() const
{
return m_command;
}
bool DvdButton::backMenu() const
{
return m_backToMenu;
}
int DvdButton::type() const
{
// Enable the use of qgraphicsitem_cast with this item.
return UserType + 1;
}
void DvdButton::setBackMenu(bool back)
{
m_backToMenu = back;
}
QVariant DvdButton::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionChange && (scene() != nullptr)) {
QPoint newPos = value.toPoint();
if (QApplication::mouseButtons() == Qt::LeftButton && (qobject_cast(scene()) != nullptr)) {
auto *customScene = qobject_cast(scene());
int gridSize = customScene->gridSize();
int xV = (newPos.x() / gridSize) * gridSize;
int yV = (newPos.y() / gridSize) * gridSize;
newPos = QPoint(xV, yV);
}
QRectF sceneShape = sceneBoundingRect();
auto *sc = static_cast(scene());
newPos.setX(qMax(newPos.x(), 0));
newPos.setY(qMax(newPos.y(), 0));
if (newPos.x() + sceneShape.width() > sc->width()) {
newPos.setX(sc->width() - sceneShape.width());
}
if (newPos.y() + sceneShape.height() > sc->height()) {
newPos.setY(sc->height() - sceneShape.height());
}
sceneShape.translate(newPos - pos());
QList list = scene()->items(sceneShape, Qt::IntersectsItemShape);
list.removeAll(this);
if (!list.isEmpty()) {
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == Type) {
return pos();
}
}
}
return newPos;
}
return QGraphicsItem::itemChange(change, value);
}
DvdWizardMenu::DvdWizardMenu(DVDFORMAT format, QWidget *parent)
: QWizardPage(parent)
, m_color(nullptr)
, m_safeRect(nullptr)
, m_finalSize(720, 576)
, m_movieLength(-1)
{
m_view.setupUi(this);
m_view.play_text->setText(i18n("Play"));
m_scene = new DvdScene(this);
m_view.menu_preview->setScene(m_scene);
m_view.menu_preview->setMouseTracking(true);
connect(m_view.create_menu, &QAbstractButton::toggled, m_view.menu_box, &QWidget::setEnabled);
connect(m_view.create_menu, &QAbstractButton::toggled, this, &QWizardPage::completeChanged);
m_view.add_button->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
m_view.delete_button->setIcon(QIcon::fromTheme(QStringLiteral("trash-empty")));
m_view.zoom_button->setIcon(QIcon::fromTheme(QStringLiteral("zoom-in")));
m_view.unzoom_button->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out")));
m_view.add_button->setToolTip(i18n("Add new button"));
m_view.delete_button->setToolTip(i18n("Delete current button"));
changeProfile(format);
// Create color background
m_color = new QGraphicsRectItem(0, 0, m_width, m_height);
m_color->setBrush(m_view.background_color->color());
m_color->setZValue(2);
m_scene->addItem(m_color);
// create background image
m_background = new QGraphicsPixmapItem();
m_background->setZValue(3);
// m_scene->addItem(m_background);
// create safe zone rect
int safeW = m_width / 20;
int safeH = m_height / 20;
m_safeRect = new QGraphicsRectItem(safeW, safeH, m_width - 2 * safeW, m_height - 2 * safeH);
QPen pen(Qt::red);
pen.setStyle(Qt::DashLine);
pen.setWidth(3);
m_safeRect->setPen(pen);
m_safeRect->setZValue(5);
m_scene->addItem(m_safeRect);
checkBackgroundType(0);
// create menu button
DvdButton *button = new DvdButton(m_view.play_text->text());
QFont font = m_view.font_family->currentFont();
font.setPixelSize(m_view.font_size->value());
// font.setStyleStrategy(QFont::NoAntialias);
if (m_view.use_shadow->isChecked()) {
auto *shadow = new QGraphicsDropShadowEffect(this);
shadow->setBlurRadius(7);
shadow->setOffset(4, 4);
button->setGraphicsEffect(shadow);
}
connect(m_view.use_shadow, &QCheckBox::stateChanged, this, &DvdWizardMenu::slotEnableShadows);
button->setFont(font);
button->setDefaultTextColor(m_view.text_color->color());
button->setZValue(4);
QRectF r = button->sceneBoundingRect();
m_scene->addItem(button);
button->setPos((m_width - r.width()) / 2, (m_height - r.height()) / 2);
button->setSelected(true);
if (m_view.use_grid->isChecked()) {
m_scene->setGridSize(10);
}
connect(m_view.use_grid, &QAbstractButton::toggled, this, &DvdWizardMenu::slotUseGrid);
// m_view.menu_preview->resizefitInView(0, 0, m_width, m_height);
connect(m_view.play_text, &QLineEdit::textChanged, this, &DvdWizardMenu::buildButton);
connect(m_view.text_color, &KColorButton::changed, this, static_cast(&DvdWizardMenu::updateColor));
connect(m_view.font_size, static_cast(&QSpinBox::valueChanged), this, &DvdWizardMenu::buildButton);
connect(m_view.font_family, &QFontComboBox::currentFontChanged, this, &DvdWizardMenu::buildButton);
connect(m_view.background_image, &KUrlRequester::textChanged, this, &DvdWizardMenu::buildImage);
connect(m_view.background_color, &KColorButton::changed, this, &DvdWizardMenu::buildColor);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(m_view.background_list, static_cast(&KComboBox::currentIndexChanged), this, &DvdWizardMenu::checkBackgroundType);
-#else
- connect(m_view.background_list, static_cast(&KComboBox::currentIndexChanged) , this, &DvdWizardMenu::checkBackgroundType);
-#endif
connect(m_view.target_list, static_cast(&KComboBox::activated), this, &DvdWizardMenu::setButtonTarget);
connect(m_view.back_to_menu, &QAbstractButton::toggled, this, &DvdWizardMenu::setBackToMenu);
connect(m_view.add_button, &QAbstractButton::pressed, this, &DvdWizardMenu::addButton);
connect(m_view.delete_button, &QAbstractButton::pressed, this, &DvdWizardMenu::deleteButton);
connect(m_view.zoom_button, &QAbstractButton::pressed, this, &DvdWizardMenu::slotZoom);
connect(m_view.unzoom_button, &QAbstractButton::pressed, this, &DvdWizardMenu::slotUnZoom);
connect(m_scene, &QGraphicsScene::selectionChanged, this, &DvdWizardMenu::buttonChanged);
connect(m_scene, &DvdScene::sceneChanged, this, &QWizardPage::completeChanged);
// red background for error message
KColorScheme scheme(palette().currentColorGroup(), KColorScheme::Window);
QPalette p = m_view.error_message->palette();
p.setColor(QPalette::Background, scheme.background(KColorScheme::NegativeBackground).color());
m_view.error_message->setAutoFillBackground(true);
m_view.error_message->setPalette(p);
m_view.menu_box->setEnabled(false);
m_menuMessage = new KMessageWidget;
auto *s = static_cast(layout());
s->addWidget(m_menuMessage, 7, 0, 1, -1);
m_menuMessage->hide();
m_view.error_message->hide();
}
DvdWizardMenu::~DvdWizardMenu()
{
delete m_color;
delete m_safeRect;
delete m_background;
delete m_scene;
}
void DvdWizardMenu::slotEnableShadows(int enable)
{
QList list = m_scene->items();
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
if (enable != 0) {
auto *shadow = new QGraphicsDropShadowEffect(this);
shadow->setBlurRadius(7);
shadow->setOffset(4, 4);
list.at(i)->setGraphicsEffect(shadow);
} else {
list.at(i)->setGraphicsEffect(nullptr);
}
}
}
}
void DvdWizardMenu::setButtonTarget(int ix)
{
QList list = m_scene->selectedItems();
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
auto *button = static_cast(list.at(i));
button->setTarget(ix, m_view.target_list->itemData(ix).toString());
break;
}
}
emit completeChanged();
}
void DvdWizardMenu::setBackToMenu(bool backToMenu)
{
QList list = m_scene->selectedItems();
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
auto *button = static_cast(list.at(i));
button->setBackMenu(backToMenu);
break;
}
}
emit completeChanged();
}
void DvdWizardMenu::buttonChanged()
{
QList list = m_scene->selectedItems();
bool foundButton = false;
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
m_view.play_text->blockSignals(true);
m_view.font_size->blockSignals(true);
m_view.font_family->blockSignals(true);
m_view.target_list->blockSignals(true);
m_view.back_to_menu->blockSignals(true);
foundButton = true;
m_view.tabWidget->widget(0)->setEnabled(true);
auto *button = static_cast(list.at(i));
m_view.target_list->setCurrentIndex(button->target());
m_view.play_text->setText(button->toPlainText());
m_view.back_to_menu->setChecked(button->backMenu());
QFont font = button->font();
m_view.font_size->setValue(font.pixelSize());
m_view.font_family->setCurrentFont(font);
m_view.play_text->blockSignals(false);
m_view.font_size->blockSignals(false);
m_view.font_family->blockSignals(false);
m_view.target_list->blockSignals(false);
m_view.back_to_menu->blockSignals(false);
break;
}
}
if (!foundButton) {
m_view.tabWidget->widget(0)->setEnabled(false);
}
}
void DvdWizardMenu::addButton()
{
m_scene->clearSelection();
DvdButton *button = new DvdButton(m_view.play_text->text());
QFont font = m_view.font_family->currentFont();
font.setPixelSize(m_view.font_size->value());
if (m_view.use_shadow->isChecked()) {
auto *shadow = new QGraphicsDropShadowEffect(this);
shadow->setBlurRadius(7);
shadow->setOffset(4, 4);
button->setGraphicsEffect(shadow);
}
// font.setStyleStrategy(QFont::NoAntialias);
button->setFont(font);
button->setDefaultTextColor(m_view.text_color->color());
button->setZValue(4);
QRectF r = button->sceneBoundingRect();
m_scene->addItem(button);
button->setPos((m_width - r.width()) / 2, (m_height - r.height()) / 2);
button->setSelected(true);
}
void DvdWizardMenu::deleteButton()
{
QList list = m_scene->selectedItems();
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
delete list.at(i);
break;
}
}
}
void DvdWizardMenu::changeProfile(DVDFORMAT format)
{
m_format = format;
switch (m_format) {
case PAL_WIDE:
m_finalSize = QSize(720, 576);
m_width = 1024;
m_height = 576;
break;
case NTSC_WIDE:
m_finalSize = QSize(720, 480);
m_width = 853;
m_height = 480;
break;
case NTSC:
m_finalSize = QSize(720, 480);
m_width = 640;
m_height = 480;
break;
default:
m_finalSize = QSize(720, 576);
m_width = 768;
m_height = 576;
}
updatePreview();
}
void DvdWizardMenu::updatePreview()
{
m_scene->setProfile(m_width, m_height);
QMatrix matrix;
matrix.scale(0.5, 0.5);
m_view.menu_preview->setMatrix(matrix);
m_view.menu_preview->setMinimumSize(m_width / 2 + 4, m_height / 2 + 8);
if (m_color) {
m_color->setRect(0, 0, m_width, m_height);
}
int safeW = m_width / 20;
int safeH = m_height / 20;
if (m_safeRect) {
m_safeRect->setRect(safeW, safeH, m_width - 2 * safeW, m_height - 2 * safeH);
}
}
void DvdWizardMenu::setTargets(const QStringList &list, const QStringList &targetlist)
{
m_view.target_list->clear();
m_view.target_list->addItem(i18n("Play All"), "jump title 1");
int movieCount = 0;
for (int i = 0; i < list.count(); ++i) {
if (targetlist.at(i).contains(QStringLiteral("chapter"))) {
m_view.target_list->addItem(list.at(i), targetlist.at(i));
} else {
m_view.target_list->addItem(QIcon::fromTheme(QStringLiteral("video-x-generic")), list.at(i), targetlist.at(i));
movieCount++;
}
}
m_view.back_to_menu->setHidden(movieCount == 1);
}
void DvdWizardMenu::checkBackgroundType(int ix)
{
if (ix == 0) {
m_view.background_color->setVisible(true);
m_view.background_image->setVisible(false);
m_view.loop_movie->setVisible(false);
if (m_background->scene() != nullptr) {
m_scene->removeItem(m_background);
}
} else {
m_view.background_color->setVisible(false);
m_view.background_image->setVisible(true);
if (ix == 1) {
m_view.background_image->clear();
m_view.background_image->setFilter(QStringLiteral("*"));
if (m_background->scene() != nullptr) {
m_scene->removeItem(m_background);
}
m_view.loop_movie->setVisible(false);
} else {
if (m_background->scene() != nullptr) {
m_scene->removeItem(m_background);
}
m_view.background_image->clear();
m_view.background_image->setMimeTypeFilters({QStringLiteral("video/mpeg")});
m_view.loop_movie->setVisible(true);
}
}
emit completeChanged();
}
void DvdWizardMenu::buildColor()
{
m_color->setBrush(m_view.background_color->color());
}
void DvdWizardMenu::slotUseGrid(bool useGrid)
{
if (useGrid) {
m_scene->setGridSize(10);
} else {
m_scene->setGridSize(1);
}
m_scene->update();
}
void DvdWizardMenu::buildImage()
{
emit completeChanged();
if (m_view.background_image->url().isEmpty()) {
if (m_background->scene() != nullptr) {
m_scene->removeItem(m_background);
}
return;
}
QPixmap pix;
if (m_view.background_list->currentIndex() == 1) {
// image background
if (!pix.load(m_view.background_image->url().toLocalFile())) {
if (m_background->scene() != nullptr) {
m_scene->removeItem(m_background);
}
return;
}
pix = pix.scaled(m_width, m_height);
} else if (m_view.background_list->currentIndex() == 2) {
// video background
m_movieLength = -1;
QString profileName = DvdWizardVob::getDvdProfile(m_format);
Mlt::Profile profile(profileName.toUtf8().constData());
profile.set_explicit(1);
Mlt::Producer *producer = new Mlt::Producer(profile, m_view.background_image->url().toLocalFile().toUtf8().constData());
if ((producer != nullptr) && producer->is_valid()) {
pix = QPixmap::fromImage(KThumb::getFrame(producer, 0, m_width, m_height));
m_movieLength = producer->get_length();
}
delete producer;
}
m_background->setPixmap(pix);
m_scene->addItem(m_background);
}
void DvdWizardMenu::buildButton()
{
DvdButton *button = nullptr;
QList list = m_scene->selectedItems();
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
button = static_cast(list.at(i));
break;
}
}
if (button == nullptr) {
return;
}
button->setPlainText(m_view.play_text->text());
QFont font = m_view.font_family->currentFont();
font.setPixelSize(m_view.font_size->value());
// font.setStyleStrategy(QFont::NoAntialias);
button->setFont(font);
button->setDefaultTextColor(m_view.text_color->color());
// Check for button overlapping in case we changed text / size
emit completeChanged();
}
void DvdWizardMenu::updateColor()
{
updateColor(m_view.text_color->color());
m_view.menu_preview->viewport()->update();
}
void DvdWizardMenu::prepareUnderLines()
{
QList list = m_scene->items();
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
QRectF r = list.at(i)->sceneBoundingRect();
int bottom = r.bottom() - 1;
if (bottom % 2 == 1) {
bottom = bottom - 1;
}
int underlineHeight = r.height() / 10;
if (underlineHeight % 2 == 1) {
underlineHeight = underlineHeight - 1;
}
underlineHeight = qMin(underlineHeight, 10);
underlineHeight = qMax(underlineHeight, 2);
r.setTop(bottom - underlineHeight);
r.setBottom(bottom);
r.adjust(2, 0, -2, 0);
auto *underline = new DvdButtonUnderline(r);
m_scene->addItem(underline);
list.at(i)->setVisible(false);
}
}
}
void DvdWizardMenu::resetUnderLines()
{
QList list = m_scene->items();
QList toDelete;
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonUnderlineItem) {
toDelete.append(list.at(i));
}
if (list.at(i)->type() == DvdButtonItem) {
list.at(i)->setVisible(true);
}
}
while (!toDelete.isEmpty()) {
QGraphicsItem *item = toDelete.takeFirst();
delete item;
}
}
void DvdWizardMenu::updateUnderlineColor(QColor c)
{
QList list = m_scene->items();
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonUnderlineItem) {
auto *underline = static_cast(list.at(i));
underline->setPen(Qt::NoPen);
c.setAlpha(150);
underline->setBrush(c);
}
}
}
void DvdWizardMenu::updateColor(const QColor &c)
{
DvdButton *button = nullptr;
QList list = m_scene->items();
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
button = static_cast(list.at(i));
button->setDefaultTextColor(c);
}
}
}
void DvdWizardMenu::createButtonImages(const QString &selected_image, const QString &highlighted_image, bool letterbox)
{
if (m_view.create_menu->isChecked()) {
m_scene->clearSelection();
QRectF source(0, 0, m_width, m_height);
QRectF target;
if (!letterbox) {
target = QRectF(0, 0, m_finalSize.width(), m_finalSize.height());
} else {
// Scale the button images to fit a letterbox image
double factor = (double)m_width / m_finalSize.width();
int letterboxHeight = m_height / factor;
target = QRectF(0, (m_finalSize.height() - letterboxHeight) / 2, m_finalSize.width(), letterboxHeight);
}
if (m_safeRect->scene() != nullptr) {
m_scene->removeItem(m_safeRect);
}
if (m_color->scene() != nullptr) {
m_scene->removeItem(m_color);
}
if (m_background->scene() != nullptr) {
m_scene->removeItem(m_background);
}
prepareUnderLines();
QImage img(m_finalSize.width(), m_finalSize.height(), QImage::Format_ARGB32);
img.fill(Qt::transparent);
updateUnderlineColor(m_view.highlighted_color->color());
QPainter p;
p.begin(&img);
// p.setRenderHints(QPainter::Antialiasing, false);
// p.setRenderHints(QPainter::TextAntialiasing, false);
m_scene->render(&p, target, source, Qt::IgnoreAspectRatio);
p.end();
img.setColor(0, m_view.highlighted_color->color().rgb());
img.setColor(1, qRgba(0, 0, 0, 0));
img.save(highlighted_image);
img.fill(Qt::transparent);
updateUnderlineColor(m_view.selected_color->color());
p.begin(&img);
// p.setRenderHints(QPainter::Antialiasing, false);
// p.setRenderHints(QPainter::TextAntialiasing, false);
m_scene->render(&p, target, source, Qt::IgnoreAspectRatio);
p.end();
img.setColor(0, m_view.selected_color->color().rgb());
img.setColor(1, qRgba(0, 0, 0, 0));
img.save(selected_image);
resetUnderLines();
m_scene->addItem(m_safeRect);
m_scene->addItem(m_color);
if (m_view.background_list->currentIndex() > 0) {
m_scene->addItem(m_background);
}
}
}
void DvdWizardMenu::createBackgroundImage(const QString &img1, bool letterbox)
{
Q_UNUSED(letterbox)
m_scene->clearSelection();
if (m_safeRect->scene() != nullptr) {
m_scene->removeItem(m_safeRect);
}
bool showBg = false;
QImage img(m_width, m_height, QImage::Format_ARGB32);
// TODO: Should the image be scaled when letterboxing?
/*
QRectF source(0, 0, m_width, m_height);
QRectF target;
if (!letterbox) target = QRectF(0, 0, m_finalSize.width(), m_finalSize.height());
else {
// Scale the button images to fit a letterbox image
double factor = (double) m_width / m_finalSize.width();
int letterboxHeight = m_height / factor;
target = QRectF(0, (m_finalSize.height() - letterboxHeight) / 2, m_finalSize.width(), letterboxHeight);
}*/
if (menuMovie()) {
showBg = true;
if (m_background->scene() != nullptr) {
m_scene->removeItem(m_background);
}
if (m_color->scene() != nullptr) {
m_scene->removeItem(m_color);
}
img.fill(Qt::transparent);
}
updateColor(m_view.text_color->color());
QPainter p(&img);
p.setRenderHints(QPainter::Antialiasing, true);
p.setRenderHints(QPainter::TextAntialiasing, true);
// set image grid to "1" to ensure we don't display dots all over
// the image when rendering
int oldSize = m_scene->gridSize();
m_scene->setGridSize(1);
m_scene->render(&p, QRectF(0, 0, img.width(), img.height()));
m_scene->setGridSize(oldSize);
// m_scene->render(&p, target, source, Qt::IgnoreAspectRatio);
p.end();
img.save(img1);
m_scene->addItem(m_safeRect);
if (showBg) {
m_scene->addItem(m_background);
m_scene->addItem(m_color);
}
}
bool DvdWizardMenu::createMenu() const
{
return m_view.create_menu->isChecked();
}
bool DvdWizardMenu::loopMovie() const
{
return m_view.loop_movie->isChecked();
}
bool DvdWizardMenu::menuMovie() const
{
return m_view.background_list->currentIndex() == 2;
}
QString DvdWizardMenu::menuMoviePath() const
{
return m_view.background_image->url().toLocalFile();
}
int DvdWizardMenu::menuMovieLength() const
{
return m_movieLength;
}
QMap DvdWizardMenu::buttonsInfo(bool letterbox)
{
QMap info;
QList list = m_scene->items();
double ratiox = (double)m_finalSize.width() / m_width;
double ratioy = 1;
int offset = 0;
if (letterbox) {
int letterboxHeight = m_height * ratiox;
ratioy = (double)letterboxHeight / m_finalSize.height();
offset = (m_finalSize.height() - letterboxHeight) / 2;
}
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
auto *button = static_cast(list.at(i));
QRectF r = button->sceneBoundingRect();
QRect adjustedRect(r.x() * ratiox, offset + r.y() * ratioy, r.width() * ratiox, r.height() * ratioy);
// Make sure y1 is not odd (requested by spumux)
if (adjustedRect.height() % 2 == 1) {
adjustedRect.setHeight(adjustedRect.height() + 1);
}
if (adjustedRect.y() % 2 == 1) {
adjustedRect.setY(adjustedRect.y() - 1);
}
QString command = button->command();
if (button->backMenu()) {
command.prepend(QStringLiteral("g1 = 999;"));
}
info.insertMulti(command, adjustedRect);
}
}
return info;
}
QDomElement DvdWizardMenu::toXml() const
{
QDomDocument doc;
QDomElement xml = doc.createElement(QStringLiteral("menu"));
doc.appendChild(xml);
xml.setAttribute(QStringLiteral("enabled"), static_cast(m_view.create_menu->isChecked()));
if (m_view.background_list->currentIndex() == 0) {
// Color bg
xml.setAttribute(QStringLiteral("background_color"), m_view.background_color->color().name());
} else if (m_view.background_list->currentIndex() == 1) {
// Image bg
xml.setAttribute(QStringLiteral("background_image"), m_view.background_image->url().toLocalFile());
} else {
// Video bg
xml.setAttribute(QStringLiteral("background_video"), m_view.background_image->url().toLocalFile());
}
xml.setAttribute(QStringLiteral("text_color"), m_view.text_color->color().name());
xml.setAttribute(QStringLiteral("selected_color"), m_view.selected_color->color().name());
xml.setAttribute(QStringLiteral("highlighted_color"), m_view.highlighted_color->color().name());
xml.setAttribute(QStringLiteral("text_shadow"), (int)m_view.use_shadow->isChecked());
QList list = m_scene->items();
int buttonCount = 0;
for (int i = 0; i < list.count(); ++i) {
if (list.at(i)->type() == DvdButtonItem) {
buttonCount++;
auto *button = static_cast(list.at(i));
QDomElement xmlbutton = doc.createElement(QStringLiteral("button"));
xmlbutton.setAttribute(QStringLiteral("target"), button->target());
xmlbutton.setAttribute(QStringLiteral("command"), button->command());
xmlbutton.setAttribute(QStringLiteral("backtomenu"), static_cast(button->backMenu()));
xmlbutton.setAttribute(QStringLiteral("posx"), (int)button->pos().x());
xmlbutton.setAttribute(QStringLiteral("posy"), (int)button->pos().y());
xmlbutton.setAttribute(QStringLiteral("text"), button->toPlainText());
QFont font = button->font();
xmlbutton.setAttribute(QStringLiteral("font_size"), font.pixelSize());
xmlbutton.setAttribute(QStringLiteral("font_family"), font.family());
xml.appendChild(xmlbutton);
}
}
return doc.documentElement();
}
void DvdWizardMenu::loadXml(DVDFORMAT format, const QDomElement &xml)
{
// qCDebug(KDENLIVE_LOG) << "// LOADING MENU";
if (xml.isNull()) {
return;
}
// qCDebug(KDENLIVE_LOG) << "// LOADING MENU 1";
changeProfile(format);
m_view.create_menu->setChecked(xml.attribute(QStringLiteral("enabled")).toInt() != 0);
if (xml.hasAttribute(QStringLiteral("background_color"))) {
m_view.background_list->setCurrentIndex(0);
m_view.background_color->setColor(xml.attribute(QStringLiteral("background_color")));
} else if (xml.hasAttribute(QStringLiteral("background_image"))) {
m_view.background_list->setCurrentIndex(1);
m_view.background_image->setUrl(QUrl(xml.attribute(QStringLiteral("background_image"))));
} else if (xml.hasAttribute(QStringLiteral("background_video"))) {
m_view.background_list->setCurrentIndex(2);
m_view.background_image->setUrl(QUrl(xml.attribute(QStringLiteral("background_video"))));
}
m_view.text_color->setColor(xml.attribute(QStringLiteral("text_color")));
m_view.selected_color->setColor(xml.attribute(QStringLiteral("selected_color")));
m_view.highlighted_color->setColor(xml.attribute(QStringLiteral("highlighted_color")));
m_view.use_shadow->setChecked(xml.attribute(QStringLiteral("text_shadow")).toInt() != 0);
QDomNodeList buttons = xml.elementsByTagName(QStringLiteral("button"));
// qCDebug(KDENLIVE_LOG) << "// LOADING MENU 2" << buttons.count();
if (!buttons.isEmpty()) {
// Clear existing buttons
for (QGraphicsItem *item : m_scene->items()) {
if (item->type() == DvdButtonItem) {
m_scene->removeItem(item);
delete item;
}
}
}
for (int i = 0; i < buttons.count(); ++i) {
QDomElement e = buttons.at(i).toElement();
// create menu button
DvdButton *button = new DvdButton(e.attribute(QStringLiteral("text")));
QFont font(e.attribute(QStringLiteral("font_family")));
font.setPixelSize(e.attribute(QStringLiteral("font_size")).toInt());
if (m_view.use_shadow->isChecked()) {
auto *shadow = new QGraphicsDropShadowEffect(this);
shadow->setBlurRadius(7);
shadow->setOffset(4, 4);
button->setGraphicsEffect(shadow);
}
// font.setStyleStrategy(QFont::NoAntialias);
button->setFont(font);
button->setTarget(e.attribute(QStringLiteral("target")).toInt(), e.attribute(QStringLiteral("command")));
button->setBackMenu(e.attribute(QStringLiteral("backtomenu")).toInt() != 0);
button->setDefaultTextColor(m_view.text_color->color());
button->setZValue(4);
m_scene->addItem(button);
button->setPos(e.attribute(QStringLiteral("posx")).toInt(), e.attribute(QStringLiteral("posy")).toInt());
}
}
void DvdWizardMenu::slotZoom()
{
m_view.menu_preview->scale(2.0, 2.0);
}
void DvdWizardMenu::slotUnZoom()
{
m_view.menu_preview->scale(0.5, 0.5);
}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 83d054340..ef411ea28 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1,4041 +1,4037 @@
/***************************************************************************
* Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include "mainwindow.h"
#include "assets/assetpanel.hpp"
#include "bin/clipcreator.hpp"
#include "bin/generators/generators.h"
#include "bin/projectclip.h"
#include "bin/projectfolder.h"
#include "bin/projectitemmodel.h"
#include "core.h"
#include "dialogs/clipcreationdialog.h"
#include "dialogs/kdenlivesettingsdialog.h"
#include "dialogs/renderwidget.h"
#include "dialogs/wizard.h"
#include "doc/docundostack.hpp"
#include "doc/kdenlivedoc.h"
#include "effects/effectlist/view/effectlistwidget.hpp"
#include "effectslist/effectbasket.h"
#include "hidetitlebars.h"
#include "jobs/jobmanager.h"
#include "jobs/scenesplitjob.hpp"
#include "jobs/speedjob.hpp"
#include "jobs/stabilizejob.hpp"
#include "jobs/transcodeclipjob.h"
#include "kdenlivesettings.h"
#include "layoutmanagement.h"
#include "library/librarywidget.h"
#include "audiomixer/mixermanager.hpp"
#include "mainwindowadaptor.h"
#include "mltconnection.h"
#include "mltcontroller/clipcontroller.h"
#include "monitor/monitor.h"
#include "monitor/monitormanager.h"
#include "monitor/scopes/audiographspectrum.h"
#include "profiles/profilemodel.hpp"
#include "project/cliptranscode.h"
#include "project/dialogs/archivewidget.h"
#include "project/dialogs/projectsettings.h"
#include "project/projectcommands.h"
#include "project/projectmanager.h"
#include "scopes/scopemanager.h"
#include "timeline2/view/timelinecontroller.h"
#include "timeline2/view/timelinetabs.hpp"
#include "timeline2/view/timelinewidget.h"
#include "titler/titlewidget.h"
#include "transitions/transitionlist/view/transitionlistwidget.hpp"
#include "transitions/transitionsrepository.hpp"
#include "utils/resourcewidget.h"
#include "utils/thememanager.h"
#include "utils/otioconvertions.h"
#include "profiles/profilerepository.hpp"
#include "widgets/progressbutton.h"
#include
#include "project/dialogs/temporarydata.h"
#ifdef USE_JOGSHUTTLE
#include "jogshuttle/jogmanager.h"
#endif
#include
#include
#include
#include
#include