diff --git a/src/dialogs/kdenlivesettingsdialog.cpp b/src/dialogs/kdenlivesettingsdialog.cpp index 44c387c18..5ae1ff2a1 100644 --- a/src/dialogs/kdenlivesettingsdialog.cpp +++ b/src/dialogs/kdenlivesettingsdialog.cpp @@ -1,1449 +1,1450 @@ /*************************************************************************** * 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 "profilesdialog.h" #include "encodingprofilesdialog.h" #include "project/dialogs/profilewidget.h" #include "utils/KoIconUtils.h" #include "dialogs/profilesdialog.h" #include "kdenlivesettings.h" #include "clipcreationdialog.h" #include "renderer.h" #ifdef USE_V4L #include "capture/v4lcapture.h" #endif #include "klocalizedstring.h" #include #include #include #include #include #include "kdenlive_debug.h" +#include #include #include #include #include #include #include #include #ifdef USE_JOGSHUTTLE #include "jogshuttle/jogaction.h" #include "jogshuttle/jogshuttleconfig.h" #include #include #endif KdenliveSettingsDialog::KdenliveSettingsDialog(const QMap &mappable_actions, bool gpuAllowed, QWidget *parent) : KConfigDialog(parent, QStringLiteral("settings"), KdenliveSettings::self()), m_modified(false), m_shuttleModified(false), m_mappable_actions(mappable_actions) { KdenliveSettings::setV4l_format(0); QWidget *p1 = new QWidget; m_configMisc.setupUi(p1); m_page1 = addPage(p1, i18n("Misc")); m_page1->setIcon(KoIconUtils::themedIcon(QStringLiteral("configure"))); // Hide avformat-novalidate trick, causes crash (bug #2205 and #2206) m_configMisc.kcfg_projectloading_avformatnovalidate->setVisible(false); 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")); QVBoxLayout *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() ? KdenliveSettings::current_profile() : KdenliveSettings::default_profile()); connect(m_pw, &ProfileWidget::profileChanged, this, &KdenliveSettingsDialog::slotDialogModified); m_page8->setIcon(KoIconUtils::themedIcon(QStringLiteral("project-defaults"))); connect(m_configProject.kcfg_generateproxy, &QAbstractButton::toggled, m_configProject.kcfg_proxyminsize, &QWidget::setEnabled); m_configProject.kcfg_proxyminsize->setEnabled(KdenliveSettings::generateproxy()); m_configProject.projecturl->setMode(KFile::Directory); m_configProject.projecturl->setUrl(QUrl::fromLocalFile(KdenliveSettings::defaultprojectfolder())); connect(m_configProject.kcfg_generateimageproxy, &QAbstractButton::toggled, m_configProject.kcfg_proxyimageminsize, &QWidget::setEnabled); m_configProject.kcfg_proxyimageminsize->setEnabled(KdenliveSettings::generateimageproxy()); QWidget *p3 = new QWidget; m_configTimeline.setupUi(p3); m_page3 = addPage(p3, i18n("Timeline")); m_page3->setIcon(KoIconUtils::themedIcon(QStringLiteral("video-display"))); 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(); qSort(mimes); m_configEnv.supportedmimes->setPlainText(mimes.join(QLatin1Char(' '))); m_page2 = addPage(p2, i18n("Environment")); m_page2->setIcon(KoIconUtils::themedIcon(QStringLiteral("application-x-executable-script"))); QWidget *p4 = new QWidget; m_configCapture.setupUi(p4); m_configCapture.tabWidget->removeTab(0); 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); } } } connect(m_configCapture.kcfg_detectedv4ldevices, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdatev4lDevice())); connect(m_configCapture.kcfg_v4l_format, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdatev4lCaptureProfile())); connect(m_configCapture.config_v4l, &QAbstractButton::clicked, this, &KdenliveSettingsDialog::slotEditVideo4LinuxProfile); slotUpdatev4lDevice(); #endif m_page4 = addPage(p4, i18n("Capture")); m_page4->setIcon(KoIconUtils::themedIcon(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(KoIconUtils::themedIcon(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(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(KoIconUtils::themedIcon(QStringLiteral("jog-dial"))); QWidget *p6 = new QWidget; m_configSdl.setupUi(p6); m_configSdl.reload_blackmagic->setIcon(KoIconUtils::themedIcon(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(KoIconUtils::themedIcon(QStringLiteral("media-playback-start"))); QWidget *p7 = new QWidget; m_configTranscode.setupUi(p7); m_page7 = addPage(p7, i18n("Transcode")); m_page7->setIcon(KoIconUtils::themedIcon(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(); connect(m_configSdl.kcfg_audio_driver, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCheckAlsaDriver())); connect(m_configSdl.kcfg_audio_backend, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCheckAudioBackend())); initDevices(); connect(m_configCapture.kcfg_grab_capture_type, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateGrabRegionStatus())); slotUpdateGrabRegionStatus(); loadTranscodeProfiles(); //HACK: check dvgrab version, because only dvgrab >= 3.3 supports // --timestamp option without bug if (KdenliveSettings::dvgrab_path().isEmpty() || !QFile::exists(KdenliveSettings::dvgrab_path())) { QString dvgrabpath = QStandardPaths::findExecutable(QStringLiteral("dvgrab")); KdenliveSettings::setDvgrab_path(dvgrabpath); } // decklink profile QAction *act = new QAction(KoIconUtils::themedIcon(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(KoIconUtils::themedIcon(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()); connect(m_configCapture.kcfg_decklink_profile, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateDecklinkProfile())); connect(m_configCapture.decklink_showprofileinfo, &QAbstractButton::clicked, m_configCapture.decklink_parameters, &QWidget::setVisible); // ffmpeg profile m_configCapture.v4l_showprofileinfo->setIcon(KoIconUtils::themedIcon(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(KoIconUtils::themedIcon(QStringLiteral("configure")), i18n("Configure profiles"), this); act->setData(2); connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile); m_configCapture.v4l_manageprofile->setDefaultAction(act); connect(m_configCapture.kcfg_v4l_profile, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateV4lProfile())); connect(m_configCapture.v4l_showprofileinfo, &QAbstractButton::clicked, m_configCapture.v4l_parameters, &QWidget::setVisible); // screen grab profile m_configCapture.grab_showprofileinfo->setIcon(KoIconUtils::themedIcon(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(KoIconUtils::themedIcon(QStringLiteral("configure")), i18n("Configure profiles"), this); act->setData(3); connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile); m_configCapture.grab_manageprofile->setDefaultAction(act); connect(m_configCapture.kcfg_grab_profile, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateGrabProfile())); connect(m_configCapture.grab_showprofileinfo, &QAbstractButton::clicked, m_configCapture.grab_parameters, &QWidget::setVisible); // Timeline preview act = new QAction(KoIconUtils::themedIcon(QStringLiteral("configure")), i18n("Configure profiles"), this); act->setData(1); connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile); m_configProject.preview_manageprofile->setDefaultAction(act); connect(m_configProject.kcfg_preview_profile, SIGNAL(currentIndexChanged(int)), this, SLOT(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(KoIconUtils::themedIcon(QStringLiteral("help-about"))); m_configProject.preview_showprofileinfo->setToolTip(i18n("Show default timeline preview parameters")); m_configProject.preview_manageprofile->setIcon(KoIconUtils::themedIcon(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_configProject.proxy_showprofileinfo->setIcon(KoIconUtils::themedIcon(QStringLiteral("help-about"))); m_configProject.proxy_showprofileinfo->setToolTip(i18n("Show default profile parameters")); m_configProject.proxy_manageprofile->setIcon(KoIconUtils::themedIcon(QStringLiteral("configure"))); m_configProject.proxy_manageprofile->setToolTip(i18n("Manage proxy profiles")); m_configProject.kcfg_proxy_profile->setToolTip(i18n("Select default proxy profile")); m_configProject.proxyparams->setVisible(false); m_configProject.proxyparams->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 3); m_configProject.proxyparams->setPlainText(KdenliveSettings::proxyparams()); act = new QAction(KoIconUtils::themedIcon(QStringLiteral("configure")), i18n("Configure profiles"), this); act->setData(0); connect(act, &QAction::triggered, this, &KdenliveSettingsDialog::slotManageEncodingProfile); m_configProject.proxy_manageprofile->setDefaultAction(act); connect(m_configProject.proxy_showprofileinfo, &QAbstractButton::clicked, m_configProject.proxyparams, &QWidget::setVisible); connect(m_configProject.kcfg_proxy_profile, SIGNAL(currentIndexChanged(int)), this, SLOT(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")); Render::getBlackMagicDeviceList(m_configCapture.kcfg_decklink_capturedevice); if (!Render::getBlackMagicOutputDeviceList(m_configSdl.kcfg_blackmagic_output_device)) { // No blackmagic card found m_configSdl.kcfg_external_display->setEnabled(false); } if (!KdenliveSettings::dvgrab_path().isEmpty()) { double dvgrabVersion = 0; QProcess *versionCheck = new QProcess; versionCheck->setProcessChannelMode(QProcess::MergedChannels); versionCheck->start(QStringLiteral("dvgrab"), QStringList() << QStringLiteral("--version")); if (versionCheck->waitForFinished()) { QString version = QString(versionCheck->readAll()).simplified(); if (version.contains(QLatin1Char(' '))) { version = version.section(QLatin1Char(' '), -1); } dvgrabVersion = version.toDouble(); //qCDebug(KDENLIVE_LOG) << "// FOUND DVGRAB VERSION: " << dvgrabVersion; } delete versionCheck; if (dvgrabVersion < 3.3) { KdenliveSettings::setFirewiretimestamp(false); m_configCapture.kcfg_firewiretimestamp->setEnabled(false); } m_configCapture.dvgrab_info->setText(i18n("dvgrab version %1 at %2", dvgrabVersion, KdenliveSettings::dvgrab_path())); } else { m_configCapture.dvgrab_info->setText(i18n("dvgrab utility not found, please install it for firewire capture")); } } 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) << "::::::::::::::::"; qSort(action_names); 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; foreach (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; foreach (KComboBox *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]]); } } #endif } KdenliveSettingsDialog::~KdenliveSettingsDialog() {} 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()); #ifndef Q_WS_MAC m_configSdl.kcfg_audio_driver->addItem(i18n("OSS"), "dsp"); m_configSdl.kcfg_audio_driver->addItem(i18n("ALSA"), "alsa"); m_configSdl.kcfg_audio_driver->addItem(i18n("PulseAudio"), "pulse"); 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), "plughw:" + QString::number(deviceId.section(QLatin1Char('-'), 0, 0).toInt()) + QLatin1Char(',') + QString::number(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(), "hw:" + QString::number(deviceId.section(QLatin1Char('-'), 0, 0).toInt()) + QLatin1Char(',') + QString::number(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(), "plughw:" + card + QLatin1Char(',') + device); m_configCapture.kcfg_v4l_alsadevice->addItem(devicestr.section(QLatin1Char(':'), -1).simplified(), "hw:" + card + QLatin1Char(',') + 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(KRun::binaryName(service->exec(), false)); } 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(KRun::binaryName(service->exec(), false)); } delete dlg; } void KdenliveSettingsDialog::slotCheckShuttle(int state) { #ifdef USE_JOGSHUTTLE m_configShuttle.config_group->setEnabled(state); 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) { setupJogshuttleBtns(m_configShuttle.shuttledevicelist->itemData(m_configShuttle.shuttledevicelist->currentIndex()).toString()); } #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); #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(); qSort(action_names); QStringList actions_map = JogShuttleConfig::actionMap(KdenliveSettings::shuttlebuttons()); QMap action_pos; foreach (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; foreach (KComboBox *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 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_dvgrabfilename->text() != KdenliveSettings::dvgrabfilename()) { KdenliveSettings::setDvgrabfilename(m_configCapture.kcfg_dvgrabfilename->text()); updateCapturePath = true; } if (m_configCapture.kcfg_firewireformat->currentIndex() != KdenliveSettings::firewireformat()) { KdenliveSettings::setFirewireformat(m_configCapture.kcfg_firewireformat->currentIndex()); updateCapturePath = true; } if (m_configCapture.kcfg_v4l_format->currentIndex() != 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->itemData(m_configCapture.kcfg_v4l_profile->currentIndex()).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->itemData(m_configCapture.kcfg_grab_profile->currentIndex()).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->itemData(m_configCapture.kcfg_decklink_profile->currentIndex()).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_configProject.kcfg_proxy_profile->itemData(m_configProject.kcfg_proxy_profile->currentIndex()).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)); } // timeline preview profilestr = m_configProject.kcfg_preview_profile->itemData(m_configProject.kcfg_preview_profile->currentIndex()).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->itemData(m_configCapture.kcfg_v4l_alsadevice->currentIndex()).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()); resetProfile = true; } value = m_configSdl.kcfg_audio_driver->itemData(m_configSdl.kcfg_audio_driver->currentIndex()).toString(); if (value != KdenliveSettings::audiodrivername()) { KdenliveSettings::setAudiodrivername(value); resetProfile = true; } if (value == QLatin1String("alsa")) { // Audio device setting is only valid for alsa driver value = m_configSdl.kcfg_audio_device->itemData(m_configSdl.kcfg_audio_device->currentIndex()).toString(); if (value != KdenliveSettings::audiodevicename()) { KdenliveSettings::setAudiodevicename(value); resetProfile = true; } } else if (KdenliveSettings::audiodevicename().isEmpty() == false) { KdenliveSettings::setAudiodevicename(QString()); resetProfile = true; } value = m_configSdl.kcfg_audio_backend->itemData(m_configSdl.kcfg_audio_backend->currentIndex()).toString(); if (value != KdenliveSettings::audiobackend()) { KdenliveSettings::setAudiobackend(value); resetProfile = true; } if (m_configSdl.kcfg_window_background->color() != KdenliveSettings::window_background()) { KdenliveSettings::setWindow_background(m_configSdl.kcfg_window_background->color()); resetProfile = true; } if (m_configSdl.kcfg_volume->value() != KdenliveSettings::volume()) { KdenliveSettings::setVolume(m_configSdl.kcfg_volume->value()); resetProfile = true; } if (m_configMisc.kcfg_tabposition->currentIndex() != KdenliveSettings::tabposition()) { KdenliveSettings::setTabposition(m_configMisc.kcfg_tabposition->currentIndex()); } 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. foreach (KComboBox *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()); } } // Mimes if (m_configEnv.kcfg_addedExtensions->text() != KdenliveSettings::addedExtensions()) { // Update list KdenliveSettings::setAddedExtensions(m_configEnv.kcfg_addedExtensions->text()); QStringList mimes = ClipCreationDialog::getExtensions(); qSort(mimes); m_configEnv.supportedmimes->setPlainText(mimes.join(QLatin1Char(' '))); } KConfigDialog::settingsChangedSlot(); //KConfigDialog::updateSettings(); if (resetProfile) { emit doResetProfile(); } if (restart) { emit restartKdenlive(); } emit checkTabPosition(); } 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_audio"))); } 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< QString, QString > profiles = transConfig.entryMap(); QMapIterator i(profiles); while (i.hasNext()) { i.next(); QListWidgetItem *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. foreach (KComboBox *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(ProfilesDialog::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(".")); } MltVideoProfile prof; if (!dir.exists(QStringLiteral("video4linux"))) { // No default formats found, build one prof.width = 320; prof.height = 200; prof.frame_rate_num = 15; prof.frame_rate_den = 1; prof.display_aspect_num = 4; prof.display_aspect_den = 3; prof.sample_aspect_num = 1; prof.sample_aspect_den = 1; prof.progressive = 1; prof.colorspace = 601; ProfilesDialog::saveProfile(prof, dir.absoluteFilePath(QStringLiteral("video4linux"))); } else { prof = ProfilesDialog::getVideoProfile(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(ProfilesDialog::getColorspaceDescription(prof.colorspace)); if (prof.progressive) { m_configCapture.p_progressive->setText(i18n("Progressive")); } } void KdenliveSettingsDialog::saveCurrentV4lProfile() { MltVideoProfile profile; profile.description = QStringLiteral("Video4Linux capture"); profile.colorspace = ProfilesDialog::getColorspaceFromDescription(m_configCapture.p_colorspace->text()); profile.width = m_configCapture.p_size->text().section('x', 0, 0).toInt(); profile.height = m_configCapture.p_size->text().section('x', 1, 1).toInt(); profile.sample_aspect_num = m_configCapture.p_aspect->text().section(QLatin1Char('/'), 0, 0).toInt(); profile.sample_aspect_den = m_configCapture.p_aspect->text().section(QLatin1Char('/'), 1, 1).toInt(); profile.display_aspect_num = m_configCapture.p_display->text().section(QLatin1Char('/'), 0, 0).toInt(); profile.display_aspect_den = m_configCapture.p_display->text().section(QLatin1Char('/'), 1, 1).toInt(); profile.frame_rate_num = m_configCapture.p_fps->text().section(QLatin1Char('/'), 0, 0).toInt(); profile.frame_rate_den = m_configCapture.p_fps->text().section(QLatin1Char('/'), 1, 1).toInt(); profile.progressive = m_configCapture.p_progressive->text() == i18n("Progressive"); QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/")); if (!dir.exists()) { dir.mkpath(QStringLiteral(".")); } ProfilesDialog::saveProfile(profile, dir.absoluteFilePath(QStringLiteral("video4linux"))); } void KdenliveSettingsDialog::slotManageEncodingProfile() { QAction *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::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< QString, QString > 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_configProject.kcfg_proxy_profile->blockSignals(true); currentItem = m_configProject.kcfg_proxy_profile->currentText(); m_configProject.kcfg_proxy_profile->clear(); KConfigGroup group4(&conf, "proxy"); values = group4.entryMap(); QMapIterator m(values); while (m.hasNext()) { m.next(); if (!m.key().isEmpty()) { m_configProject.kcfg_proxy_profile->addItem(m.key(), m.value()); } } if (!currentItem.isEmpty()) { m_configProject.kcfg_proxy_profile->setCurrentIndex(m_configProject.kcfg_proxy_profile->findText(currentItem)); } m_configProject.kcfg_proxy_profile->blockSignals(false); profilestr = m_configProject.kcfg_proxy_profile->itemData(m_configProject.kcfg_proxy_profile->currentIndex()).toString(); if (profilestr.isEmpty()) { m_configProject.proxyparams->clear(); } else { m_configProject.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_configProject.kcfg_proxy_profile->currentIndex(); } QString profilestr = m_configProject.kcfg_proxy_profile->itemData(ix).toString(); if (profilestr.isEmpty()) { return; } m_configProject.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() { Render::getBlackMagicDeviceList(m_configCapture.kcfg_decklink_capturedevice, true); if (!Render::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() ? KdenliveSettings::current_profile() : 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 } diff --git a/src/dvdwizard/dvdwizardvob.cpp b/src/dvdwizard/dvdwizardvob.cpp index 6f3e30b3d..78f617a7b 100644 --- a/src/dvdwizard/dvdwizardvob.cpp +++ b/src/dvdwizard/dvdwizardvob.cpp @@ -1,854 +1,855 @@ /*************************************************************************** * 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 "dvdwizardvob.h" #include "doc/kthumb.h" #include "kdenlivesettings.h" #include "timecode.h" #include "dialogs/clipcreationdialog.h" #include #include "kdenlive_debug.h" #include #include #include #include "klocalizedstring.h" #include #include +#include #include #include #include #include #include #include #include #include DvdTreeWidget::DvdTreeWidget(QWidget *parent) : QTreeWidget(parent) { setAcceptDrops(true); } void DvdTreeWidget::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls()) { event->setDropAction(Qt::CopyAction); event->setAccepted(true); } else { QTreeWidget::dragEnterEvent(event); } } void DvdTreeWidget::dragMoveEvent(QDragMoveEvent *event) { event->acceptProposedAction(); } void DvdTreeWidget::mouseDoubleClickEvent(QMouseEvent *) { emit addNewClip(); } void DvdTreeWidget::dropEvent(QDropEvent *event) { QList clips = event->mimeData()->urls(); event->accept(); emit addClips(clips); } DvdWizardVob::DvdWizardVob(QWidget *parent) : QWizardPage(parent) , m_installCheck(true) , m_duration(0) { m_view.setupUi(this); m_view.button_add->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); m_view.button_delete->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); m_view.button_up->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); m_view.button_down->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); m_vobList = new DvdTreeWidget(this); QVBoxLayout *lay1 = new QVBoxLayout; lay1->setMargin(0); lay1->addWidget(m_vobList); m_view.list_frame->setLayout(lay1); m_vobList->setColumnCount(3); m_vobList->setHeaderHidden(true); m_view.convert_box->setVisible(false); connect(m_vobList, SIGNAL(addClips(QList)), this, SLOT(slotAddVobList(QList))); connect(m_vobList, SIGNAL(addNewClip()), this, SLOT(slotAddVobList())); connect(m_view.button_add, SIGNAL(clicked()), this, SLOT(slotAddVobList())); connect(m_view.button_delete, &QAbstractButton::clicked, this, &DvdWizardVob::slotDeleteVobFile); connect(m_view.button_up, &QAbstractButton::clicked, this, &DvdWizardVob::slotItemUp); connect(m_view.button_down, &QAbstractButton::clicked, this, &DvdWizardVob::slotItemDown); connect(m_view.convert_abort, &QAbstractButton::clicked, this, &DvdWizardVob::slotAbortTranscode); connect(m_vobList, &QTreeWidget::itemSelectionChanged, this, &DvdWizardVob::slotCheckVobList); m_vobList->setIconSize(QSize(60, 45)); QString errorMessage; if (QStandardPaths::findExecutable(QStringLiteral("dvdauthor")).isEmpty()) { errorMessage.append(i18n("Program %1 is required for the DVD wizard.", i18n("dvdauthor"))); } if (QStandardPaths::findExecutable(QStringLiteral("mkisofs")).isEmpty() && QStandardPaths::findExecutable(QStringLiteral("genisoimage")).isEmpty()) { errorMessage.append(i18n("Program %1 or %2 is required for the DVD wizard.", i18n("mkisofs"), i18n("genisoimage"))); } if (!errorMessage.isEmpty()) { m_view.button_add->setEnabled(false); m_view.dvd_profile->setEnabled(false); } m_view.dvd_profile->addItems(QStringList() << i18n("PAL 4:3") << i18n("PAL 16:9") << i18n("NTSC 4:3") << i18n("NTSC 16:9")); connect(m_view.dvd_profile, SIGNAL(activated(int)), this, SLOT(slotCheckProfiles())); m_vobList->header()->setStretchLastSection(false); m_vobList->header()->setSectionResizeMode(0, QHeaderView::Stretch); m_vobList->header()->setSectionResizeMode(1, QHeaderView::Custom); m_vobList->header()->setSectionResizeMode(2, QHeaderView::Custom); m_capacityBar = new KCapacityBar(KCapacityBar::DrawTextInline, this); QHBoxLayout *lay = new QHBoxLayout; lay->addWidget(m_capacityBar); m_view.size_box->setLayout(lay); m_vobList->setItemDelegate(new DvdViewDelegate(m_vobList)); m_transcodeAction = new QAction(i18n("Transcode"), this); connect(m_transcodeAction, &QAction::triggered, this, &DvdWizardVob::slotTranscodeFiles); m_warnMessage = new KMessageWidget; m_warnMessage->setCloseButtonVisible(false); QGridLayout *s = static_cast (layout()); s->addWidget(m_warnMessage, 2, 0, 1, -1); if (!errorMessage.isEmpty()) { m_warnMessage->setMessageType(KMessageWidget::Error); m_warnMessage->setText(errorMessage); m_installCheck = false; } else { m_warnMessage->setMessageType(KMessageWidget::Warning); m_warnMessage->setText(i18n("Your clips do not match selected DVD format, transcoding required.")); m_warnMessage->addAction(m_transcodeAction); m_warnMessage->hide(); } m_view.button_transcode->setHidden(true); slotCheckVobList(); m_transcodeProcess.setProcessChannelMode(QProcess::MergedChannels); connect(&m_transcodeProcess, &QProcess::readyReadStandardOutput, this, &DvdWizardVob::slotShowTranscodeInfo); connect(&m_transcodeProcess, static_cast(&QProcess::finished), this, &DvdWizardVob::slotTranscodeFinished); } DvdWizardVob::~DvdWizardVob() { delete m_capacityBar; // Abort running transcoding if (m_transcodeProcess.state() != QProcess::NotRunning) { disconnect(&m_transcodeProcess, static_cast(&QProcess::finished), this, &DvdWizardVob::slotTranscodeFinished); m_transcodeProcess.close(); m_transcodeProcess.waitForFinished(); } } bool DvdWizardVob::isComplete() const { return m_vobList->topLevelItemCount() > 0; } void DvdWizardVob::slotShowTranscodeInfo() { QString log = QString(m_transcodeProcess.readAll()); if (m_duration == 0) { if (log.contains(QStringLiteral("Duration:"))) { QString durationstr = log.section(QStringLiteral("Duration:"), 1, 1).section(QLatin1Char(','), 0, 0).simplified(); const QStringList numbers = durationstr.split(QLatin1Char(':')); if (numbers.size() < 3) { return; } m_duration = numbers.at(0).toInt() * 3600 + numbers.at(1).toInt() * 60 + numbers.at(2).toDouble(); //log_text->setHidden(true); //job_progress->setHidden(false); } else { //log_text->setHidden(false); //job_progress->setHidden(true); } } else if (log.contains(QStringLiteral("time="))) { int progress; QString time = log.section(QStringLiteral("time="), 1, 1).simplified().section(QLatin1Char(' '), 0, 0); if (time.contains(QLatin1Char(':'))) { const QStringList numbers = time.split(QLatin1Char(':')); if (numbers.size() < 3) { return; } progress = numbers.at(0).toInt() * 3600 + numbers.at(1).toInt() * 60 + numbers.at(2).toDouble(); } else { progress = (int) time.toDouble(); } m_view.convert_progress->setValue((int)(100.0 * progress / m_duration)); } //log_text->setPlainText(log); } void DvdWizardVob::slotAbortTranscode() { if (m_transcodeProcess.state() != QProcess::NotRunning) { m_transcodeProcess.close(); m_transcodeProcess.waitForFinished(); } m_transcodeQueue.clear(); m_view.convert_box->hide(); slotCheckProfiles(); } void DvdWizardVob::slotTranscodeFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode == 0 && exitStatus == QProcess::NormalExit) { slotTranscodedClip(m_currentTranscoding.filename, m_currentTranscoding.filename + m_currentTranscoding.params.section(QStringLiteral("%1"), 1, 1).section(QLatin1Char(' '), 0, 0)); if (!m_transcodeQueue.isEmpty()) { m_transcodeProcess.close(); processTranscoding(); return; } } else { // Something failed //TODO show log m_warnMessage->setMessageType(KMessageWidget::Warning); m_warnMessage->setText(i18n("Transcoding failed!")); m_warnMessage->animatedShow(); m_transcodeQueue.clear(); } m_duration = 0; m_transcodeProcess.close(); if (m_transcodeQueue.isEmpty()) { m_view.convert_box->setHidden(true); slotCheckProfiles(); } } void DvdWizardVob::slotCheckProfiles() { bool conflict = false; int comboProfile = m_view.dvd_profile->currentIndex(); for (int i = 0; i < m_vobList->topLevelItemCount(); ++i) { QTreeWidgetItem *item = m_vobList->topLevelItem(i); if (item->data(0, Qt::UserRole + 1).toInt() != comboProfile) { conflict = true; break; } } m_transcodeAction->setEnabled(conflict); if (conflict) { showProfileError(); } else { m_warnMessage->animatedHide(); } } void DvdWizardVob::slotAddVobList(QList list) { if (list.isEmpty()) { QString allExtensions = ClipCreationDialog::getExtensions().join(QLatin1Char(' ')); QString dialogFilter = i18n("All Supported Files") + QStringLiteral(" (") + allExtensions + QStringLiteral(");; ") + i18n("MPEG Files") + QStringLiteral(" (*.mpeg *.mpg *.vob);; ") + i18n("All Files") + QStringLiteral(" (*.*)"); list = QFileDialog::getOpenFileUrls(this, i18n("Add new video file"), QUrl::fromLocalFile(KRecentDirs::dir(QStringLiteral(":KdenliveDvdFolder"))), dialogFilter); if (!list.isEmpty()) { KRecentDirs::add(QStringLiteral(":KdenliveDvdFolder"), list.at(0).adjusted(QUrl::RemoveFilename).toLocalFile()); } } foreach (const QUrl &url, list) { slotAddVobFile(url, QString(), false); } slotCheckVobList(); slotCheckProfiles(); } void DvdWizardVob::slotAddVobFile(const QUrl &url, const QString &chapters, bool checkFormats) { if (!url.isValid()) { return; } QFile f(url.toLocalFile()); qint64 fileSize = f.size(); Mlt::Profile profile; profile.set_explicit(false); QTreeWidgetItem *item = new QTreeWidgetItem(m_vobList, QStringList() << url.toLocalFile() << QString() << KIO::convertSize(static_cast(fileSize))); item->setData(2, Qt::UserRole, fileSize); item->setData(0, Qt::DecorationRole, QIcon::fromTheme(QStringLiteral("video-x-generic")).pixmap(60, 45)); item->setToolTip(0, url.toLocalFile()); QString resource = url.toLocalFile(); resource.prepend(QStringLiteral("avformat:")); Mlt::Producer *producer = new Mlt::Producer(profile, resource.toUtf8().data()); if (producer && producer->is_valid() && !producer->is_blank()) { double fps = profile.fps(); profile.from_producer(*producer); profile.set_explicit(true); if (profile.fps() != fps) { // fps changed, rebuild producer delete producer; producer = new Mlt::Producer(profile, resource.toUtf8().data()); } } if (producer && producer->is_valid() && !producer->is_blank()) { int width = 45.0 * profile.dar(); if (width % 2 == 1) { width++; } item->setData(0, Qt::DecorationRole, QPixmap::fromImage(KThumb::getFrame(producer, 0, width, 45))); int playTime = producer->get_playtime(); item->setText(1, Timecode::getStringTimecode(playTime, profile.fps())); item->setData(1, Qt::UserRole, playTime); int standard = -1; int aspect = profile.dar() * 100; if (profile.height() == 576 && profile.fps() == 25.0) { if (aspect > 150) { standard = 1; } else { standard = 0; } } else if (profile.height() == 480 && qAbs(profile.fps() - 30000.0 / 1001) < 0.2) { if (aspect > 150) { standard = 3; } else { standard = 2; } } QString standardName; switch (standard) { case 3: standardName = i18n("NTSC 16:9"); break; case 2: standardName = i18n("NTSC 4:3"); break; case 1: standardName = i18n("PAL 16:9"); break; case 0: standardName = i18n("PAL 4:3"); break; default: standardName = i18n("Unknown"); } standardName.append(QStringLiteral(" | %1x%2, %3fps").arg(profile.width()).arg(profile.height()).arg(profile.fps())); item->setData(0, Qt::UserRole, standardName); item->setData(0, Qt::UserRole + 1, standard); item->setData(0, Qt::UserRole + 2, QSize(profile.dar() * profile.height(), profile.height())); if (m_vobList->topLevelItemCount() == 1) { // This is the first added movie, auto select DVD format if (standard >= 0) { m_view.dvd_profile->blockSignals(true); m_view.dvd_profile->setCurrentIndex(standard); m_view.dvd_profile->blockSignals(false); } } } else { // Cannot load movie, reject showError(i18n("The clip %1 is invalid.", url.fileName())); } delete producer; if (chapters.isEmpty() == false) { item->setData(1, Qt::UserRole + 1, chapters); } else if (QFile::exists(url.toLocalFile() + QStringLiteral(".dvdchapter"))) { // insert chapters as children QFile file(url.toLocalFile() + QStringLiteral(".dvdchapter")); if (file.open(QIODevice::ReadOnly)) { QDomDocument doc; if (doc.setContent(&file) == false) { file.close(); return; } file.close(); QDomNodeList chapterNodes = doc.elementsByTagName(QStringLiteral("chapter")); QStringList chaptersList; for (int j = 0; j < chapterNodes.count(); ++j) { chaptersList.append(QString::number(chapterNodes.at(j).toElement().attribute(QStringLiteral("time")).toInt())); } item->setData(1, Qt::UserRole + 1, chaptersList.join(QLatin1Char(';'))); } } else { // Explicitly add a chapter at 00:00:00:00 item->setData(1, Qt::UserRole + 1, "0"); } if (checkFormats) { slotCheckVobList(); slotCheckProfiles(); } } void DvdWizardVob::slotDeleteVobFile() { QTreeWidgetItem *item = m_vobList->currentItem(); if (item == nullptr) { return; } delete item; slotCheckVobList(); slotCheckProfiles(); } void DvdWizardVob::setUrl(const QString &url) { slotAddVobFile(QUrl::fromLocalFile(url)); } QStringList DvdWizardVob::selectedUrls() const { QStringList result; int max = m_vobList->topLevelItemCount(); int i = 0; if (m_view.use_intro->isChecked()) { // First movie is only for intro i = 1; } for (; i < max; ++i) { QTreeWidgetItem *item = m_vobList->topLevelItem(i); if (item) { result.append(item->text(0)); } } return result; } QStringList DvdWizardVob::durations() const { QStringList result; int max = m_vobList->topLevelItemCount(); int i = 0; if (m_view.use_intro->isChecked()) { // First movie is only for intro i = 1; } for (; i < max; ++i) { QTreeWidgetItem *item = m_vobList->topLevelItem(i); if (item) { result.append(QString::number(item->data(1, Qt::UserRole).toInt())); } } return result; } QStringList DvdWizardVob::chapters() const { QStringList result; int max = m_vobList->topLevelItemCount(); int i = 0; if (m_view.use_intro->isChecked()) { // First movie is only for intro i = 1; } for (; i < max; ++i) { QTreeWidgetItem *item = m_vobList->topLevelItem(i); if (item) { result.append(item->data(1, Qt::UserRole + 1).toString()); } } return result; } void DvdWizardVob::updateChapters(const QMap &chaptersdata) { int max = m_vobList->topLevelItemCount(); int i = 0; if (m_view.use_intro->isChecked()) { // First movie is only for intro i = 1; } for (; i < max; ++i) { QTreeWidgetItem *item = m_vobList->topLevelItem(i); if (chaptersdata.contains(item->text(0))) { item->setData(1, Qt::UserRole + 1, chaptersdata.value(item->text(0))); } } } int DvdWizardVob::duration(int ix) const { int result = -1; QTreeWidgetItem *item = m_vobList->topLevelItem(ix); if (item) { result = item->data(1, Qt::UserRole).toInt(); } return result; } const QString DvdWizardVob::introMovie() const { QString url; if (m_view.use_intro->isChecked() && m_vobList->topLevelItemCount() > 0) { url = m_vobList->topLevelItem(0)->text(0); } return url; } void DvdWizardVob::setUseIntroMovie(bool use) { m_view.use_intro->setChecked(use); } void DvdWizardVob::slotCheckVobList() { emit completeChanged(); int max = m_vobList->topLevelItemCount(); QTreeWidgetItem *item = m_vobList->currentItem(); bool hasItem = true; if (item == nullptr) { hasItem = false; } m_view.button_delete->setEnabled(hasItem); if (hasItem && m_vobList->indexOfTopLevelItem(item) == 0) { m_view.button_up->setEnabled(false); } else { m_view.button_up->setEnabled(hasItem); } if (hasItem && m_vobList->indexOfTopLevelItem(item) == max - 1) { m_view.button_down->setEnabled(false); } else { m_view.button_down->setEnabled(hasItem); } qint64 totalSize = 0; for (int i = 0; i < max; ++i) { item = m_vobList->topLevelItem(i); if (item) { totalSize += (qint64) item->data(2, Qt::UserRole).toInt(); } } qint64 maxSize = (qint64) 47000 * 100000; m_capacityBar->setValue(static_cast(100 * totalSize / maxSize)); m_capacityBar->setText(KIO::convertSize(static_cast(totalSize))); } void DvdWizardVob::slotItemUp() { QTreeWidgetItem *item = m_vobList->currentItem(); if (item == nullptr) { return; } int index = m_vobList->indexOfTopLevelItem(item); if (index == 0) { return; } m_vobList->insertTopLevelItem(index - 1, m_vobList->takeTopLevelItem(index)); } void DvdWizardVob::slotItemDown() { int max = m_vobList->topLevelItemCount(); QTreeWidgetItem *item = m_vobList->currentItem(); if (item == nullptr) { return; } int index = m_vobList->indexOfTopLevelItem(item); if (index == max - 1) { return; } m_vobList->insertTopLevelItem(index + 1, m_vobList->takeTopLevelItem(index)); } DVDFORMAT DvdWizardVob::dvdFormat() const { return (DVDFORMAT) m_view.dvd_profile->currentIndex(); } const QString DvdWizardVob::dvdProfile() const { QString profile; switch (m_view.dvd_profile->currentIndex()) { case PAL_WIDE: profile = QStringLiteral("dv_pal_wide"); break; case NTSC: profile = QStringLiteral("dv_ntsc"); break; case NTSC_WIDE: profile = QStringLiteral("dv_ntsc_wide"); break; default: profile = QStringLiteral("dv_pal"); } return profile; } //static QString DvdWizardVob::getDvdProfile(DVDFORMAT format) { QString profile; switch (format) { case PAL_WIDE: profile = QStringLiteral("dv_pal_wide"); break; case NTSC: profile = QStringLiteral("dv_ntsc"); break; case NTSC_WIDE: profile = QStringLiteral("dv_ntsc_wide"); break; default: profile = QStringLiteral("dv_pal"); } return profile; } void DvdWizardVob::setProfile(const QString &profile) { if (profile == QLatin1String("dv_pal_wide")) { m_view.dvd_profile->setCurrentIndex(PAL_WIDE); } else if (profile == QLatin1String("dv_ntsc")) { m_view.dvd_profile->setCurrentIndex(NTSC); } else if (profile == QLatin1String("dv_ntsc_wide")) { m_view.dvd_profile->setCurrentIndex(NTSC_WIDE); } else { m_view.dvd_profile->setCurrentIndex(PAL); } } void DvdWizardVob::clear() { m_vobList->clear(); } void DvdWizardVob::slotTranscodeFiles() { m_warnMessage->animatedHide(); // Find transcoding info related to selected DVD profile KSharedConfigPtr config = KSharedConfig::openConfig(QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("kdenlivetranscodingrc")), KConfig::CascadeConfig); KConfigGroup transConfig(config, "Transcoding"); // read the entries QString profileEasyName; QSize destSize; QSize finalSize; switch (m_view.dvd_profile->currentIndex()) { case PAL_WIDE: profileEasyName = QStringLiteral("DVD PAL 16:9"); destSize = QSize(1024, 576); finalSize = QSize(720, 576); break; case NTSC: profileEasyName = QStringLiteral("DVD NTSC 4:3"); destSize = QSize(640, 480); finalSize = QSize(720, 480); break; case NTSC_WIDE: profileEasyName = QStringLiteral("DVD NTSC 16:9"); destSize = QSize(853, 480); finalSize = QSize(720, 480); break; default: profileEasyName = QStringLiteral("DVD PAL 4:3"); destSize = QSize(768, 576); finalSize = QSize(720, 576); } QString params = transConfig.readEntry(profileEasyName); m_transcodeQueue.clear(); m_view.convert_progress->setValue(0); m_duration = 0; // Transcode files that do not match selected profile int max = m_vobList->topLevelItemCount(); int format = m_view.dvd_profile->currentIndex(); m_view.convert_box->setVisible(true); for (int i = 0; i < max; ++i) { QTreeWidgetItem *item = m_vobList->topLevelItem(i); if (item->data(0, Qt::UserRole + 1).toInt() != format) { // File needs to be transcoded m_transcodeAction->setEnabled(false); QSize original = item->data(0, Qt::UserRole + 2).toSize(); double input_aspect = (double) original.width() / original.height(); QStringList postParams; if (input_aspect > (double) destSize.width() / destSize.height()) { // letterboxing int conv_height = (int)(destSize.width() / input_aspect); int conv_pad = (int)(((double)(destSize.height() - conv_height)) / 2.0); if (conv_pad % 2 == 1) { conv_pad --; } postParams << QStringLiteral("-vf") << QStringLiteral("scale=%1:%2,pad=%3:%4:0:%5,setdar=%6").arg(finalSize.width()).arg(destSize.height() - 2 * conv_pad).arg(finalSize.width()).arg(finalSize.height()).arg(conv_pad).arg(input_aspect); } else { // pillarboxing int conv_width = (int)(destSize.height() * input_aspect); int conv_pad = (int)(((double)(destSize.width() - conv_width)) / destSize.width() * finalSize.width() / 2.0); if (conv_pad % 2 == 1) { conv_pad --; } postParams << QStringLiteral("-vf") << QStringLiteral("scale=%1:%2,pad=%3:%4:%5:0,setdar=%6").arg(finalSize.width() - 2 * conv_pad).arg(destSize.height()).arg(finalSize.width()).arg(finalSize.height()).arg(conv_pad).arg(input_aspect); } TranscodeJobInfo jobInfo; jobInfo.filename = item->text(0); jobInfo.params = params.section(QLatin1Char(';'), 0, 0); jobInfo.postParams = postParams; m_transcodeQueue << jobInfo; } } processTranscoding(); } void DvdWizardVob::processTranscoding() { if (m_transcodeQueue.isEmpty()) { return; } m_currentTranscoding = m_transcodeQueue.takeFirst(); QStringList parameters; QStringList postParams = m_currentTranscoding.postParams; QString params = m_currentTranscoding.params; QString extension = params.section(QStringLiteral("%1"), 1, 1).section(QLatin1Char(' '), 0, 0); parameters << QStringLiteral("-i") << m_currentTranscoding.filename; if (QFile::exists(m_currentTranscoding.filename + extension)) { if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", m_currentTranscoding.filename + extension)) == KMessageBox::No) { // TODO inform about abortion m_transcodeQueue.clear(); return; } parameters << QStringLiteral("-y"); } bool replaceVfParams = false; const QStringList splitted = params.split(QLatin1Char(' ')); foreach (QString s, splitted) { if (replaceVfParams) { parameters << postParams.at(1); replaceVfParams = false; } else if (s.startsWith(QLatin1String("%1"))) { parameters << s.replace(0, 2, m_currentTranscoding.filename); } else if (!postParams.isEmpty() && s == QLatin1String("-vf")) { replaceVfParams = true; parameters << s; } else { parameters << s; } } qCDebug(KDENLIVE_LOG) << " / / /STARTING TCODE JB: \n" << KdenliveSettings::ffmpegpath() << " = " << parameters; m_transcodeProcess.start(KdenliveSettings::ffmpegpath(), parameters); m_view.convert_label->setText(i18n("Transcoding: %1", QUrl::fromLocalFile(m_currentTranscoding.filename).fileName())); } void DvdWizardVob::slotTranscodedClip(const QString &src, const QString &transcoded) { if (transcoded.isEmpty()) { // Transcoding canceled or failed m_transcodeAction->setEnabled(true); return; } int max = m_vobList->topLevelItemCount(); for (int i = 0; i < max; ++i) { QTreeWidgetItem *item = m_vobList->topLevelItem(i); if (QUrl::fromLocalFile(item->text(0)).toLocalFile() == src) { // Replace movie with transcoded version item->setText(0, transcoded); QFile f(transcoded); qint64 fileSize = f.size(); Mlt::Profile profile; profile.set_explicit(false); item->setText(2, KIO::convertSize(static_cast(fileSize))); item->setData(2, Qt::UserRole, fileSize); item->setData(0, Qt::DecorationRole, QIcon::fromTheme(QStringLiteral("video-x-generic")).pixmap(60, 45)); item->setToolTip(0, transcoded); QString resource = transcoded; resource.prepend(QStringLiteral("avformat:")); Mlt::Producer *producer = new Mlt::Producer(profile, resource.toUtf8().data()); if (producer && producer->is_valid() && !producer->is_blank()) { double fps = profile.fps(); profile.from_producer(*producer); profile.set_explicit(true); if (profile.fps() != fps) { // fps changed, rebuild producer delete producer; producer = new Mlt::Producer(profile, resource.toUtf8().data()); } } if (producer && producer->is_valid() && !producer->is_blank()) { int width = 45.0 * profile.dar(); if (width % 2 == 1) { width++; } item->setData(0, Qt::DecorationRole, QPixmap::fromImage(KThumb::getFrame(producer, 0, width, 45))); int playTime = producer->get_playtime(); item->setText(1, Timecode::getStringTimecode(playTime, profile.fps())); item->setData(1, Qt::UserRole, playTime); int standard = -1; int aspect = profile.dar() * 100; if (profile.height() == 576) { if (aspect > 150) { standard = 1; } else { standard = 0; } } else if (profile.height() == 480) { if (aspect > 150) { standard = 3; } else { standard = 2; } } QString standardName; switch (standard) { case 3: standardName = i18n("NTSC 16:9"); break; case 2: standardName = i18n("NTSC 4:3"); break; case 1: standardName = i18n("PAL 16:9"); break; case 0: standardName = i18n("PAL 4:3"); break; default: standardName = i18n("Unknown"); } item->setData(0, Qt::UserRole, standardName); item->setData(0, Qt::UserRole + 1, standard); item->setData(0, Qt::UserRole + 2, QSize(profile.dar() * profile.height(), profile.height())); } else { // Cannot load movie, reject showError(i18n("The clip %1 is invalid.", transcoded)); } delete producer; slotCheckVobList(); if (m_transcodeQueue.isEmpty()) { slotCheckProfiles(); } break; } } } void DvdWizardVob::showProfileError() { m_warnMessage->setText(i18n("Your clips do not match selected DVD format, transcoding required.")); m_warnMessage->setCloseButtonVisible(false); m_warnMessage->addAction(m_transcodeAction); m_warnMessage->animatedShow(); } void DvdWizardVob::showError(const QString &error) { m_warnMessage->setText(error); m_warnMessage->setCloseButtonVisible(true); m_warnMessage->removeAction(m_transcodeAction); m_warnMessage->animatedShow(); } diff --git a/src/project/dialogs/profilewidget.cpp b/src/project/dialogs/profilewidget.cpp index 00502bac3..cc655068c 100644 --- a/src/project/dialogs/profilewidget.cpp +++ b/src/project/dialogs/profilewidget.cpp @@ -1,249 +1,250 @@ /* Copyright (C) 2016 Jean-Baptiste Mardelle Copyright (C) 2017 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 "profilewidget.h" #include "profiles/tree/profiletreemodel.hpp" #include "profiles/tree/profilefilter.hpp" #include "profiles/profilerepository.hpp" #include "profiles/profilemodel.hpp" #include "utils/KoIconUtils.h" #include "kxmlgui_version.h" #include #include +#include #include #include #include ProfileWidget::ProfileWidget(QWidget *parent) : QWidget(parent) { m_originalProfile = QStringLiteral("invalid"); setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); QVBoxLayout *lay = new QVBoxLayout; QHBoxLayout *labelLay = new QHBoxLayout; QLabel* fpsLabel = new QLabel(i18n("Fps"),this); fpsFilt = new QComboBox(this); fpsLabel->setBuddy(fpsFilt); labelLay->addWidget(fpsLabel); labelLay->addWidget(fpsFilt); QLabel* scanningLabel = new QLabel(i18n("Scanning"),this); scanningFilt = new QComboBox(this); scanningLabel->setBuddy(scanningFilt); labelLay->addWidget(scanningLabel); labelLay->addWidget(scanningFilt); labelLay->addStretch(1); QToolButton *manage_profiles = new QToolButton(this); labelLay->addWidget(manage_profiles); manage_profiles->setIcon(KoIconUtils::themedIcon(QStringLiteral("configure"))); manage_profiles->setToolTip(i18n("Manage project profiles")); connect(manage_profiles, &QAbstractButton::clicked, this, &ProfileWidget::slotEditProfiles); lay->addLayout(labelLay); QSplitter *profileSplitter = new QSplitter; m_treeView = new QTreeView(this); m_treeModel = new ProfileTreeModel(this); m_filter = new ProfileFilter(this); m_filter->setSourceModel(m_treeModel); m_treeView->setModel(m_filter); for (int i = 1; i < m_treeModel->columnCount(); ++i) { m_treeView->hideColumn(i); } m_treeView->header()->hide(); QItemSelectionModel *selectionModel = m_treeView->selectionModel(); connect(selectionModel, &QItemSelectionModel::currentRowChanged, this, &ProfileWidget::slotChangeSelection); connect(selectionModel, &QItemSelectionModel::selectionChanged, [&](const QItemSelection &selected, const QItemSelection &deselected){ QModelIndex current, old; if (!selected.indexes().isEmpty()) { current = selected.indexes().front(); } if (!deselected.indexes().isEmpty()) { old = deselected.indexes().front(); } slotChangeSelection(current, old); }); profileSplitter->addWidget(m_treeView); m_treeView->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); m_descriptionPanel = new QTextEdit(this); m_descriptionPanel->setReadOnly(true); m_descriptionPanel->viewport()->setCursor(Qt::ArrowCursor); m_descriptionPanel->viewport()->setBackgroundRole(QPalette::Mid); m_descriptionPanel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); m_descriptionPanel->setFrameStyle(QFrame::NoFrame); profileSplitter->addWidget(m_descriptionPanel); lay->addWidget(profileSplitter); profileSplitter->setStretchFactor(0, 4); profileSplitter->setStretchFactor(1, 3); auto all_fps = ProfileRepository::get()->getAllFps(); QLocale locale; locale.setNumberOptions(QLocale::OmitGroupSeparator); fpsFilt->addItem("Any", -1); for (double fps : all_fps) { fpsFilt->addItem(locale.toString(fps), fps); } auto updateFps = [&]() { double current = fpsFilt->currentData().toDouble(); KdenliveSettings::setProfile_fps_filter(fpsFilt->currentText()); m_filter->setFilterFps(current > 0, current); slotFilterChanged(); }; connect(fpsFilt, static_cast(&QComboBox::currentIndexChanged), updateFps); int ix = fpsFilt->findText(KdenliveSettings::profile_fps_filter()); if (ix > -1) { fpsFilt->setCurrentIndex(ix); } scanningFilt->addItem("Any", -1); scanningFilt->addItem("Interlaced", 0); scanningFilt->addItem("Progressive", 1); auto updateScanning = [&]() { int current = scanningFilt->currentData().toInt(); KdenliveSettings::setProfile_scanning_filter(scanningFilt->currentText()); m_filter->setFilterInterlaced(current != -1, current == 0); slotFilterChanged(); }; connect(scanningFilt, static_cast(&QComboBox::currentIndexChanged), updateScanning); ix = scanningFilt->findText(KdenliveSettings::profile_scanning_filter()); if (ix > -1) { scanningFilt->setCurrentIndex(ix); } setLayout(lay); } ProfileWidget::~ProfileWidget() { } void ProfileWidget::loadProfile(const QString& profile) { auto index = m_treeModel->findProfile(profile); if (index.isValid()) { m_originalProfile = m_currentProfile = m_lastValidProfile = profile; if (!trySelectProfile(profile)) { // When loading a profile, ensure it is visible so reset filters if necessary fpsFilt->setCurrentIndex(0); scanningFilt->setCurrentIndex(0); } } } const QString ProfileWidget::selectedProfile() const { return m_currentProfile; } void ProfileWidget::slotEditProfiles() { ProfilesDialog *w = new ProfilesDialog(m_currentProfile); w->exec(); loadProfile(m_currentProfile); delete w; } void ProfileWidget::fillDescriptionPanel(const QString& profile_path) { QString description; if (profile_path.isEmpty()) { description += i18n("No profile selected"); } else { std::unique_ptr & profile = ProfileRepository::get()->getProfile(profile_path); description += i18n("
Video Settings
"); description += i18n("

Frame size: %1 x %2 (%3:%4)
",profile->width(), profile->height(), profile->display_aspect_num(), profile->display_aspect_den()); description += i18n("Frame rate: %1 fps
",profile->fps()); description += i18n("Pixel Aspect Ratio: %1
",profile->sar()); description += i18n("Color Space: %1
",profile->colorspaceDescription()); QString interlaced = i18n("yes"); if (profile->progressive()) { interlaced = i18n("no"); } description += i18n("Interlaced : %1

", interlaced); } m_descriptionPanel->setHtml(description); } void ProfileWidget::slotChangeSelection(const QModelIndex ¤t, const QModelIndex &previous) { auto originalIndex = m_filter->mapToSource(current); if (m_treeModel->parent(originalIndex) == QModelIndex()) { //in that case, we have selected a category, which we don't want QItemSelectionModel *selection = m_treeView->selectionModel(); selection->select(previous, QItemSelectionModel::Select); return; } m_currentProfile = ProfileTreeModel::getProfile(originalIndex); if (!m_currentProfile.isEmpty()) { m_lastValidProfile = m_currentProfile; } if (m_originalProfile != m_currentProfile) { emit profileChanged(); } fillDescriptionPanel(m_currentProfile); } bool ProfileWidget::trySelectProfile(const QString& profile) { auto index = m_treeModel->findProfile(profile); if (index.isValid()) { // check if element is visible if (m_filter->isVisible(index)) { //reselect QItemSelectionModel *selection = m_treeView->selectionModel(); selection->select(m_filter->mapFromSource(index), QItemSelectionModel::Select); //expand corresponding category auto parent = m_treeModel->parent(index); m_treeView->expand(m_filter->mapFromSource(parent)); m_treeView->scrollTo(m_filter->mapFromSource(index), QAbstractItemView::PositionAtCenter); return true; } else { return false; } } else { } return false; } void ProfileWidget::slotFilterChanged() { //When filtering change, we must check if the current profile is still visible. if (!trySelectProfile(m_currentProfile)) { //we try to back-up the last valid profile if (!trySelectProfile(m_lastValidProfile)) { //Everything fails, we don't have any profile m_currentProfile = QString(); emit profileChanged(); fillDescriptionPanel(QString()); } } } diff --git a/src/project/jobs/jobmanager.cpp b/src/project/jobs/jobmanager.cpp index 526816a7b..ee0feb757 100644 --- a/src/project/jobs/jobmanager.cpp +++ b/src/project/jobs/jobmanager.cpp @@ -1,364 +1,365 @@ /* Copyright (C) 2014 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 "jobmanager.h" #include "kdenlivesettings.h" #include "doc/kdenlivedoc.h" #include "abstractclipjob.h" #include "proxyclipjob.h" #include "cutclipjob.h" #include "bin/projectclip.h" #include "project/clipstabilize.h" #include "meltjob.h" #include "filterjob.h" #include "bin/bin.h" #include "mlt++/Mlt.h" #include "kdenlive_debug.h" +#include #include #include #include #include "ui_scenecutdialog_ui.h" JobManager::JobManager(Bin *bin): QObject() , m_bin(bin) , m_abortAllJobs(false) { connect(this, &JobManager::processLog, this, &JobManager::slotProcessLog); connect(this, &JobManager::checkJobProcess, this, &JobManager::slotCheckJobProcess); } JobManager::~JobManager() { m_abortAllJobs = true; for (int i = 0; i < m_jobList.count(); ++i) { m_jobList.at(i)->setStatus(JobAborted); } m_jobThreads.waitForFinished(); m_jobThreads.clearFutures(); if (!m_jobList.isEmpty()) { qDeleteAll(m_jobList); } m_jobList.clear(); } void JobManager::slotProcessLog(const QString &id, int progress, int type, const QString &message) { ProjectClip *item = m_bin->getBinClip(id); item->setJobStatus((AbstractClipJob::JOBTYPE) type, JobWorking, progress, message); } QStringList JobManager::getPendingJobs(const QString &id) { QStringList result; QMutexLocker lock(&m_jobMutex); for (int i = 0; i < m_jobList.count(); ++i) { if (m_jobList.at(i)->clipId() == id && (m_jobList.at(i)->status() == JobWaiting || m_jobList.at(i)->status() == JobWorking)) { // discard this job result << m_jobList.at(i)->description; } } return result; } void JobManager::discardJobs(const QString &id, AbstractClipJob::JOBTYPE type) { QMutexLocker lock(&m_jobMutex); for (int i = 0; i < m_jobList.count(); ++i) { if (m_jobList.at(i)->clipId() == id && (type == AbstractClipJob::NOJOBTYPE || m_jobList.at(i)->jobType == type)) { // discard this job m_jobList.at(i)->setStatus(JobAborted); } } emit updateJobStatus(id, type, JobAborted); updateJobCount(); } bool JobManager::hasPendingJob(const QString &clipId, AbstractClipJob::JOBTYPE type) { QMutexLocker lock(&m_jobMutex); for (int i = 0; i < m_jobList.count(); i++) { if (m_abortAllJobs) { break; } AbstractClipJob *job = m_jobList.at(i); if (job->clipId() == clipId && job->jobType == type && (job->status() == JobWaiting || job->status() == JobWorking)) { return true; } } return false; } void JobManager::slotCheckJobProcess() { if (!m_jobThreads.futures().isEmpty()) { // Remove inactive threads QList > futures = m_jobThreads.futures(); m_jobThreads.clearFutures(); for (int i = 0; i < futures.count(); ++i) if (!futures.at(i).isFinished()) { m_jobThreads.addFuture(futures.at(i)); } } if (m_jobList.isEmpty()) { return; } m_jobMutex.lock(); int count = 0; for (int i = 0; i < m_jobList.count(); ++i) { if (m_jobList.at(i)->status() == JobWorking || m_jobList.at(i)->status() == JobWaiting) { count ++; } else { // remove finished jobs AbstractClipJob *job = m_jobList.takeAt(i); job->deleteLater(); --i; } } m_jobMutex.unlock(); emit jobCount(count); if (m_jobThreads.futures().isEmpty() || m_jobThreads.futures().count() < KdenliveSettings::proxythreads()) { m_jobThreads.addFuture(QtConcurrent::run(this, &JobManager::slotProcessJobs)); } } void JobManager::updateJobCount() { int count = 0; for (int i = 0; i < m_jobList.count(); ++i) { if (m_jobList.at(i)->status() == JobWaiting || m_jobList.at(i)->status() == JobWorking) { count ++; } } // Set jobs count emit jobCount(count); } void JobManager::slotProcessJobs() { bool firstPass = true; while (!m_jobList.isEmpty() && !m_abortAllJobs) { AbstractClipJob *job = nullptr; m_jobMutex.lock(); for (int i = 0; i < m_jobList.count(); ++i) { if (m_jobList.at(i)->status() == JobWaiting) { job = m_jobList.at(i); job->setStatus(JobWorking); break; } } if (!firstPass) { updateJobCount(); } m_jobMutex.unlock(); if (job == nullptr) { break; } firstPass = false; QString destination = job->destination(); // Check if the clip is still here ProjectClip *currentClip = m_bin->getBinClip(job->clipId()); if (currentClip == nullptr) { job->setStatus(JobDone); continue; } // Set clip status to started currentClip->setJobStatus(job->jobType, job->status()); // Make sure destination path is writable if (!destination.isEmpty()) { QFileInfo file(destination); bool writable = false; if (file.exists()) { if (file.isWritable()) { writable = true; } } else { QDir dir = file.absoluteDir(); if (!dir.exists()) { writable = dir.mkpath(QStringLiteral(".")); } else { QFileInfo dinfo(dir.absolutePath()); writable = dinfo.isWritable(); } } if (!writable) { emit updateJobStatus(job->clipId(), job->jobType, JobCrashed, i18n("Cannot write to path: %1", destination)); job->setStatus(JobCrashed); continue; } } connect(job, SIGNAL(jobProgress(QString, int, int)), this, SIGNAL(processLog(QString, int, int))); connect(job, &AbstractClipJob::cancelRunningJob, m_bin, &Bin::slotCancelRunningJob); if (job->jobType == AbstractClipJob::MLTJOB || job->jobType == AbstractClipJob::ANALYSECLIPJOB) { connect(job, SIGNAL(gotFilterJobResults(QString, int, int, stringMap, stringMap)), this, SIGNAL(gotFilterJobResults(QString, int, int, stringMap, stringMap))); } job->startJob(); if (job->status() == JobDone) { emit updateJobStatus(job->clipId(), job->jobType, JobDone); //TODO: replace with more generic clip replacement framework if (job->jobType == AbstractClipJob::PROXYJOB) { m_bin->gotProxy(job->clipId(), destination); } else if (job->addClipToProject() > -100) { emit addClip(destination, job->addClipToProject()); } } else if (job->status() == JobCrashed || job->status() == JobAborted) { emit updateJobStatus(job->clipId(), job->jobType, job->status(), job->errorMessage(), QString(), job->logDetails()); } } // Thread finished, cleanup & update count QTimer::singleShot(200, this, &JobManager::checkJobProcess); } QList JobManager::filterClips(const QList &clips, AbstractClipJob::JOBTYPE jobType, const QStringList ¶ms) { //TODO: filter depending on clip type if (jobType == AbstractClipJob::TRANSCODEJOB || jobType == AbstractClipJob::CUTJOB) { return CutClipJob::filterClips(clips, params); } else if (jobType == AbstractClipJob::FILTERCLIPJOB) { return FilterJob::filterClips(clips, params); } else if (jobType == AbstractClipJob::PROXYJOB) { return ProxyJob::filterClips(clips); } return QList (); } void JobManager::prepareJobFromTimeline(ProjectClip *clip, const QMap &producerParams, const QMap &filterParams, const QMap &consumerParams, const QMap &extraParams) { MeltJob *job = new MeltJob(clip->clipType(), clip->clipId(), producerParams, filterParams, consumerParams, extraParams); job->description = i18n("Filter %1", extraParams.value(QStringLiteral("finalfilter"))); launchJob(clip, job); } void JobManager::prepareJobs(const QList &clips, double fps, AbstractClipJob::JOBTYPE jobType, const QStringList ¶ms) { //TODO filter clips QList matching = filterClips(clips, jobType, params); if (matching.isEmpty()) { m_bin->doDisplayMessage(i18n("No valid clip to process"), KMessageWidget::Information); return; } QHash jobs; if (jobType == AbstractClipJob::TRANSCODEJOB) { jobs = CutClipJob::prepareTranscodeJob(fps, matching, params); } else if (jobType == AbstractClipJob::CUTJOB) { ProjectClip *clip = matching.constFirst(); double originalFps = clip->getOriginalFps(); jobs = CutClipJob::prepareCutClipJob(fps, originalFps, clip); } else if (jobType == AbstractClipJob::ANALYSECLIPJOB) { jobs = CutClipJob::prepareAnalyseJob(fps, matching, params); } else if (jobType == AbstractClipJob::FILTERCLIPJOB) { jobs = FilterJob::prepareJob(matching, params); } else if (jobType == AbstractClipJob::PROXYJOB) { jobs = ProxyJob::prepareJob(m_bin, matching); } if (!jobs.isEmpty()) { QHashIterator i(jobs); while (i.hasNext()) { i.next(); launchJob(i.key(), i.value(), false); } slotCheckJobProcess(); } } void JobManager::launchJob(ProjectClip *clip, AbstractClipJob *job, bool runQueue) { if (job->isExclusive() && hasPendingJob(clip->clipId(), job->jobType)) { delete job; return; } m_jobList.append(job); clip->setJobStatus(job->jobType, JobWaiting, 0, job->statusMessage()); if (runQueue) { slotCheckJobProcess(); } } void JobManager::slotDiscardClipJobs() { QAction *act = qobject_cast(sender()); if (act == nullptr) { // Cannot access triggering action, something is wrong qCDebug(KDENLIVE_LOG) << "// Error in job action"; return; } QString id = act->data().toString(); if (id.isEmpty()) { return; } discardJobs(id); } void JobManager::slotCancelPendingJobs() { QMutexLocker lock(&m_jobMutex); for (int i = 0; i < m_jobList.count(); ++i) { if (m_jobList.at(i)->status() == JobWaiting) { // discard this job m_jobList.at(i)->setStatus(JobAborted); emit updateJobStatus(m_jobList.at(i)->clipId(), m_jobList.at(i)->jobType, JobAborted); } } updateJobCount(); } void JobManager::slotCancelJobs() { m_abortAllJobs = true; for (int i = 0; i < m_jobList.count(); ++i) { m_jobList.at(i)->setStatus(JobAborted); } m_jobThreads.waitForFinished(); m_jobThreads.clearFutures(); //TODO: undo job cancelation ? not sure it's necessary /*QUndoCommand *command = new QUndoCommand(); command->setText(i18np("Cancel job", "Cancel jobs", m_jobList.count())); m_jobMutex.lock(); for (int i = 0; i < m_jobList.count(); ++i) { DocClipBase *currentClip = m_doc->clipManager()->getClipById(m_jobList.at(i)->clipId()); if (!currentClip) continue; QMap newProps = m_jobList.at(i)->cancelProperties(); if (newProps.isEmpty()) continue; QMap oldProps = currentClip->currentProperties(newProps); //TODO //new EditClipCommand(this, m_jobList.at(i)->clipId(), oldProps, newProps, true, command); } m_jobMutex.unlock(); if (command->childCount() > 0) { m_doc->commandStack()->push(command); } else delete command; */ if (!m_jobList.isEmpty()) { qDeleteAll(m_jobList); } m_jobList.clear(); m_abortAllJobs = false; emit jobCount(0); } diff --git a/src/titler/titlewidget.cpp b/src/titler/titlewidget.cpp index 9517544a3..d4e34322d 100644 --- a/src/titler/titlewidget.cpp +++ b/src/titler/titlewidget.cpp @@ -1,2937 +1,2938 @@ /*************************************************************************** titlewidget.cpp - description ------------------- begin : Feb 28 2008 copyright : (C) 2008 by Marco Gittler email : g.marco@freenet.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "titlewidget.h" #include "gradientwidget.h" #include "kdenlivesettings.h" #include "doc/kthumb.h" #include "renderer.h" #include "KoSliderCombo.h" #include "utils/KoIconUtils.h" #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 #include static QList titletemplates; // What exactly is this variable good for? int settingUp = false; const int IMAGEITEM = 7; const int RECTITEM = 3; const int TEXTITEM = 8; const int NOEFFECT = 0; const int BLUREFFECT = 1; const int SHADOWEFFECT = 2; const int TYPEWRITEREFFECT = 3; TitleWidget::TitleWidget(const QUrl &url, const Timecode &tc, const QString &projectTitlePath, Render *render, QWidget *parent) : QDialog(parent), Ui::TitleWidget_UI(), m_startViewport(nullptr), m_endViewport(nullptr), m_count(0), m_unicodeDialog(new UnicodeDialog(UnicodeDialog::InputHex)), m_projectTitlePath(projectTitlePath), m_tc(tc), m_fps(render->fps()) { setupUi(this); setMinimumSize(200, 200); setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); frame_properties->setEnabled(false); frame_properties->setFixedHeight(frame_toolbar->height()); int size = style()->pixelMetric(QStyle::PM_SmallIconSize); QSize iconSize(size, size); rectBColor->setAlphaChannelEnabled(true); rectFColor->setAlphaChannelEnabled(true); fontColorButton->setAlphaChannelEnabled(true); textOutlineColor->setAlphaChannelEnabled(true); shadowColor->setAlphaChannelEnabled(true); QButtonGroup *colorGroup = new QButtonGroup(this); colorGroup->addButton(gradient_color); colorGroup->addButton(plain_color); QButtonGroup *alignGroup = new QButtonGroup(this); alignGroup->addButton(buttonAlignLeft); alignGroup->addButton(buttonAlignCenter); alignGroup->addButton(buttonAlignRight); textOutline->setMinimum(0); textOutline->setMaximum(200); //textOutline->setDecimals(0); textOutline->setValue(0); textOutline->setToolTip(i18n("Outline width")); backgroundAlpha->setMinimum(0); backgroundAlpha->setMaximum(255); bgAlphaSlider->setMinimum(0); bgAlphaSlider->setMaximum(255); backgroundAlpha->setValue(0); backgroundAlpha->setToolTip(i18n("Background color opacity")); itemrotatex->setMinimum(-360); itemrotatex->setMaximum(360); //itemrotatex->setDecimals(0); itemrotatex->setValue(0); itemrotatex->setToolTip(i18n("Rotation around the X axis")); itemrotatey->setMinimum(-360); itemrotatey->setMaximum(360); //itemrotatey->setDecimals(0); itemrotatey->setValue(0); itemrotatey->setToolTip(i18n("Rotation around the Y axis")); itemrotatez->setMinimum(-360); itemrotatez->setMaximum(360); //itemrotatez->setDecimals(0); itemrotatez->setValue(0); itemrotatez->setToolTip(i18n("Rotation around the Z axis")); rectLineWidth->setMinimum(0); rectLineWidth->setMaximum(500); //rectLineWidth->setDecimals(0); rectLineWidth->setValue(0); rectLineWidth->setToolTip(i18n("Border width")); itemzoom->setSuffix(i18n("%")); m_frameWidth = render->renderWidth(); m_frameHeight = render->renderHeight(); showToolbars(TITLE_SELECT); splitter->setStretchFactor(0, 20); //If project is drop frame, set the input mask as such. title_duration->setInputMask(m_tc.mask()); title_duration->setText(m_tc.reformatSeparators(KdenliveSettings::title_duration())); connect(backgroundColor, &KColorButton::changed, this, &TitleWidget::slotChangeBackground); connect(backgroundAlpha, SIGNAL(valueChanged(int)), this, SLOT(slotChangeBackground())); connect(shadowBox, &QGroupBox::toggled, this, &TitleWidget::slotUpdateShadow); connect(shadowColor, &KColorButton::changed, this, &TitleWidget::slotUpdateShadow); connect(blur_radius, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateShadow())); connect(shadowX, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateShadow())); connect(shadowY, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateShadow())); connect(fontColorButton, &KColorButton::changed, this, &TitleWidget::slotUpdateText); connect(plain_color, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); connect(gradient_color, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); connect(gradients_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateText())); connect(textOutlineColor, &KColorButton::changed, this, &TitleWidget::slotUpdateText); connect(font_family, &QFontComboBox::currentFontChanged, this, &TitleWidget::slotUpdateText); connect(font_size, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText())); connect(letter_spacing, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText())); connect(line_spacing, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText())); connect(textOutline, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateText())); connect(font_weight_box, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateText())); connect(rectFColor, &KColorButton::changed, this, &TitleWidget::rectChanged); connect(rectBColor, &KColorButton::changed, this, &TitleWidget::rectChanged); connect(plain_rect, &QAbstractButton::clicked, this, &TitleWidget::rectChanged); connect(gradient_rect, &QAbstractButton::clicked, this, &TitleWidget::rectChanged); connect(gradients_rect_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(rectChanged())); connect(rectLineWidth, SIGNAL(valueChanged(int)), this, SLOT(rectChanged())); // Fill effects, NOT SUPPORTED in titler version 2 /*effect_list->addItem(i18n("None"), NOEFFECT); if (render->getMltVersionInfo(QStringLiteral("kdenlivetitle")) > 1.0) { // there was a bug in MLT's kdenlivetitle module version 1 that crashed on typewriter effect effect_list->addItem(i18n("Typewriter"), TYPEWRITEREFFECT); } effect_list->addItem(i18n("Blur"), BLUREFFECT);*/ connect(zValue, SIGNAL(valueChanged(int)), this, SLOT(zIndexChanged(int))); connect(itemzoom, SIGNAL(valueChanged(int)), this, SLOT(itemScaled(int))); connect(itemrotatex, SIGNAL(valueChanged(int)), this, SLOT(itemRotateX(int))); connect(itemrotatey, SIGNAL(valueChanged(int)), this, SLOT(itemRotateY(int))); connect(itemrotatez, SIGNAL(valueChanged(int)), this, SLOT(itemRotateZ(int))); connect(itemhcenter, &QAbstractButton::clicked, this, &TitleWidget::itemHCenter); connect(itemvcenter, &QAbstractButton::clicked, this, &TitleWidget::itemVCenter); connect(itemtop, &QAbstractButton::clicked, this, &TitleWidget::itemTop); connect(itembottom, &QAbstractButton::clicked, this, &TitleWidget::itemBottom); connect(itemleft, &QAbstractButton::clicked, this, &TitleWidget::itemLeft); connect(itemright, &QAbstractButton::clicked, this, &TitleWidget::itemRight); connect(effect_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotAddEffect(int))); connect(typewriter_delay, SIGNAL(valueChanged(int)), this, SLOT(slotEditTypewriter(int))); connect(typewriter_start, SIGNAL(valueChanged(int)), this, SLOT(slotEditTypewriter(int))); connect(origin_x_left, &QAbstractButton::clicked, this, &TitleWidget::slotOriginXClicked); connect(origin_y_top, &QAbstractButton::clicked, this, &TitleWidget::slotOriginYClicked); connect(render, &AbstractRender::frameUpdated, this, &TitleWidget::slotGotBackground); // Position and size m_signalMapper = new QSignalMapper(this); m_signalMapper->setMapping(value_w, ValueWidth); m_signalMapper->setMapping(value_h, ValueHeight); m_signalMapper->setMapping(value_x, ValueX); m_signalMapper->setMapping(value_y, ValueY); connect(value_w, SIGNAL(valueChanged(int)), m_signalMapper, SLOT(map())); connect(value_h, SIGNAL(valueChanged(int)), m_signalMapper, SLOT(map())); connect(value_x, SIGNAL(valueChanged(int)), m_signalMapper, SLOT(map())); connect(value_y, SIGNAL(valueChanged(int)), m_signalMapper, SLOT(map())); connect(m_signalMapper, SIGNAL(mapped(int)), this, SLOT(slotValueChanged(int))); connect(buttonFitZoom, &QAbstractButton::clicked, this, &TitleWidget::slotAdjustZoom); connect(buttonRealSize, &QAbstractButton::clicked, this, &TitleWidget::slotZoomOneToOne); connect(buttonItalic, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); connect(buttonUnder, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); connect(buttonAlignLeft, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); connect(buttonAlignRight, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); connect(buttonAlignCenter, &QAbstractButton::clicked, this, &TitleWidget::slotUpdateText); connect(edit_gradient, &QAbstractButton::clicked, this, &TitleWidget::slotEditGradient); connect(edit_rect_gradient, &QAbstractButton::clicked, this, &TitleWidget::slotEditGradient); connect(displayBg, &QCheckBox::stateChanged, this, &TitleWidget::displayBackgroundFrame); connect(m_unicodeDialog, &UnicodeDialog::charSelected, this, &TitleWidget::slotInsertUnicodeString); // mbd connect(this, &QDialog::accepted, this, &TitleWidget::slotAccepted); font_weight_box->blockSignals(true); font_weight_box->addItem(i18nc("Font style", "Light"), QFont::Light); font_weight_box->addItem(i18nc("Font style", "Normal"), QFont::Normal); font_weight_box->addItem(i18nc("Font style", "Demi-Bold"), QFont::DemiBold); font_weight_box->addItem(i18nc("Font style", "Bold"), QFont::Bold); font_weight_box->addItem(i18nc("Font style", "Black"), QFont::Black); font_weight_box->setToolTip(i18n("Font weight")); font_weight_box->setCurrentIndex(1); font_weight_box->blockSignals(false); buttonFitZoom->setIconSize(iconSize); buttonRealSize->setIconSize(iconSize); buttonItalic->setIconSize(iconSize); buttonUnder->setIconSize(iconSize); buttonAlignCenter->setIconSize(iconSize); buttonAlignLeft->setIconSize(iconSize); buttonAlignRight->setIconSize(iconSize); buttonFitZoom->setIcon(KoIconUtils::themedIcon(QStringLiteral("zoom-fit-best"))); buttonRealSize->setIcon(KoIconUtils::themedIcon(QStringLiteral("zoom-original"))); buttonItalic->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-text-italic"))); buttonUnder->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-text-underline"))); buttonAlignCenter->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-justify-center"))); buttonAlignLeft->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-justify-left"))); buttonAlignRight->setIcon(KoIconUtils::themedIcon(QStringLiteral("format-justify-right"))); edit_gradient->setIcon(KoIconUtils::themedIcon(QStringLiteral("document-edit"))); edit_rect_gradient->setIcon(KoIconUtils::themedIcon(QStringLiteral("document-edit"))); buttonAlignRight->setToolTip(i18n("Align right")); buttonAlignLeft->setToolTip(i18n("Align left")); buttonAlignCenter->setToolTip(i18n("Align center")); buttonAlignLeft->setChecked(true); m_unicodeAction = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-insert-unicode")), QString(), this); m_unicodeAction->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_U); m_unicodeAction->setToolTip(getTooltipWithShortcut(i18n("Insert Unicode character"), m_unicodeAction)); connect(m_unicodeAction, &QAction::triggered, this, &TitleWidget::slotInsertUnicode); buttonInsertUnicode->setDefaultAction(m_unicodeAction); m_zUp = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-up")), QString(), this); m_zUp->setShortcut(Qt::Key_PageUp); m_zUp->setToolTip(i18n("Raise object")); connect(m_zUp, &QAction::triggered, this, &TitleWidget::slotZIndexUp); zUp->setDefaultAction(m_zUp); m_zDown = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-down")), QString(), this); m_zDown->setShortcut(Qt::Key_PageDown); m_zDown->setToolTip(i18n("Lower object")); connect(m_zDown, &QAction::triggered, this, &TitleWidget::slotZIndexDown); zDown->setDefaultAction(m_zDown); m_zTop = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-top")), QString(), this); // TODO mbt 1414: Shortcut should change z index only if // cursor is NOT in a text field ... //m_zTop->setShortcut(Qt::Key_Home); m_zTop->setToolTip(i18n("Raise object to top")); connect(m_zTop, &QAction::triggered, this, &TitleWidget::slotZIndexTop); zTop->setDefaultAction(m_zTop); m_zBottom = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-bottom")), QString(), this); // TODO mbt 1414 //m_zBottom->setShortcut(Qt::Key_End); m_zBottom->setToolTip(i18n("Lower object to bottom")); connect(m_zBottom, &QAction::triggered, this, &TitleWidget::slotZIndexBottom); zBottom->setDefaultAction(m_zBottom); m_selectAll = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-select-all")), QString(), this); m_selectAll->setShortcut(Qt::CTRL + Qt::Key_A); connect(m_selectAll, &QAction::triggered, this, &TitleWidget::slotSelectAll); buttonSelectAll->setDefaultAction(m_selectAll); m_selectText = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-select-texts")), QString(), this); m_selectText->setShortcut(Qt::CTRL + Qt::Key_T); connect(m_selectText, &QAction::triggered, this, &TitleWidget::slotSelectText); buttonSelectText->setDefaultAction(m_selectText); buttonSelectText->setEnabled(false); m_selectRects = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-select-rects")), QString(), this); m_selectRects->setShortcut(Qt::CTRL + Qt::Key_R); connect(m_selectRects, &QAction::triggered, this, &TitleWidget::slotSelectRects); buttonSelectRects->setDefaultAction(m_selectRects); buttonSelectRects->setEnabled(false); m_selectImages = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-select-images")), QString(), this); m_selectImages->setShortcut(Qt::CTRL + Qt::Key_I); connect(m_selectImages, &QAction::triggered, this, &TitleWidget::slotSelectImages); buttonSelectImages->setDefaultAction(m_selectImages); buttonSelectImages->setEnabled(false); m_unselectAll = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-unselect-all")), QString(), this); m_unselectAll->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_A); connect(m_unselectAll, &QAction::triggered, this, &TitleWidget::slotSelectNone); buttonUnselectAll->setDefaultAction(m_unselectAll); buttonUnselectAll->setEnabled(false); zDown->setIconSize(iconSize); zTop->setIconSize(iconSize); zBottom->setIconSize(iconSize); zDown->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-down"))); zTop->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-top"))); zBottom->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-zindex-bottom"))); connect(zDown, &QAbstractButton::clicked, this, &TitleWidget::slotZIndexDown); connect(zTop, &QAbstractButton::clicked, this, &TitleWidget::slotZIndexTop); connect(zBottom, &QAbstractButton::clicked, this, &TitleWidget::slotZIndexBottom); origin_x_left->setToolTip(i18n("Invert x axis and change 0 point")); origin_y_top->setToolTip(i18n("Invert y axis and change 0 point")); rectBColor->setToolTip(i18n("Select fill color")); rectFColor->setToolTip(i18n("Select border color")); zoom_slider->setToolTip(i18n("Zoom")); buttonRealSize->setToolTip(i18n("Original size (1:1)")); buttonFitZoom->setToolTip(i18n("Fit zoom")); backgroundColor->setToolTip(i18n("Select background color")); backgroundAlpha->setToolTip(i18n("Background opacity")); buttonSelectAll->setToolTip(getTooltipWithShortcut(i18n("Select all"), m_selectAll)); buttonSelectText->setToolTip(getTooltipWithShortcut(i18n("Select text items in current selection"), m_selectText)); buttonSelectRects->setToolTip(getTooltipWithShortcut(i18n("Select rect items in current selection"), m_selectRects)); buttonSelectImages->setToolTip(getTooltipWithShortcut(i18n("Select image items in current selection"), m_selectImages)); buttonUnselectAll->setToolTip(getTooltipWithShortcut(i18n("Unselect all"), m_unselectAll)); itemhcenter->setIconSize(iconSize); itemvcenter->setIconSize(iconSize); itemtop->setIconSize(iconSize); itembottom->setIconSize(iconSize); itemright->setIconSize(iconSize); itemleft->setIconSize(iconSize); itemhcenter->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-hor"))); itemhcenter->setToolTip(i18n("Align item horizontally")); itemvcenter->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-vert"))); itemvcenter->setToolTip(i18n("Align item vertically")); itemtop->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-top"))); itemtop->setToolTip(i18n("Align item to top")); itembottom->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-bottom"))); itembottom->setToolTip(i18n("Align item to bottom")); itemright->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-right"))); itemright->setToolTip(i18n("Align item to right")); itemleft->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-left"))); itemleft->setToolTip(i18n("Align item to left")); QHBoxLayout *layout = new QHBoxLayout; frame_toolbar->setLayout(layout); layout->setContentsMargins(0, 0, 0, 0); QToolBar *m_toolbar = new QToolBar(QStringLiteral("titleToolBar"), this); m_toolbar->setIconSize(iconSize); m_buttonCursor = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("transform-move")), i18n("Selection Tool")); m_buttonCursor->setCheckable(true); m_buttonCursor->setShortcut(Qt::ALT + Qt::Key_S); m_buttonCursor->setToolTip(i18n("Selection Tool") + QLatin1Char(' ') + m_buttonCursor->shortcut().toString()); connect(m_buttonCursor, &QAction::triggered, this, &TitleWidget::slotSelectTool); m_buttonText = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("insert-text")), i18n("Add Text")); m_buttonText->setCheckable(true); m_buttonText->setShortcut(Qt::ALT + Qt::Key_T); m_buttonText->setToolTip(i18n("Add Text") + QLatin1Char(' ') + m_buttonText->shortcut().toString()); connect(m_buttonText, &QAction::triggered, this, &TitleWidget::slotTextTool); m_buttonRect = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-insert-rect")), i18n("Add Rectangle")); m_buttonRect->setCheckable(true); m_buttonRect->setShortcut(Qt::ALT + Qt::Key_R); m_buttonRect->setToolTip(i18n("Add Rectangle") + QLatin1Char(' ') + m_buttonRect->shortcut().toString()); connect(m_buttonRect, &QAction::triggered, this, &TitleWidget::slotRectTool); m_buttonImage = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("insert-image")), i18n("Add Image")); m_buttonImage->setCheckable(false); m_buttonImage->setShortcut(Qt::ALT + Qt::Key_I); m_buttonImage->setToolTip(i18n("Add Image") + QLatin1Char(' ') + m_buttonImage->shortcut().toString()); connect(m_buttonImage, &QAction::triggered, this, &TitleWidget::slotImageTool); m_toolbar->addSeparator(); m_buttonLoad = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("document-open")), i18n("Open Document")); m_buttonLoad->setCheckable(false); m_buttonLoad->setShortcut(Qt::CTRL + Qt::Key_O); m_buttonLoad->setToolTip(i18n("Open Document") + QLatin1Char(' ') + m_buttonLoad->shortcut().toString()); connect(m_buttonLoad, SIGNAL(triggered()), this, SLOT(loadTitle())); m_buttonSave = m_toolbar->addAction(KoIconUtils::themedIcon(QStringLiteral("document-save-as")), i18n("Save As")); m_buttonSave->setCheckable(false); m_buttonSave->setShortcut(Qt::CTRL + Qt::Key_S); m_buttonSave->setToolTip(i18n("Save As") + QLatin1Char(' ') + m_buttonSave->shortcut().toString()); connect(m_buttonSave, SIGNAL(triggered()), this, SLOT(saveTitle())); layout->addWidget(m_toolbar); // initialize graphic scene m_scene = new GraphicsSceneRectMove(this); graphicsView->setScene(m_scene); graphicsView->setMouseTracking(true); graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); graphicsView->setDragMode(QGraphicsView::RubberBandDrag); graphicsView->setRubberBandSelectionMode(Qt::ContainsItemBoundingRect); m_titledocument.setScene(m_scene, m_frameWidth, m_frameHeight); connect(m_scene, &QGraphicsScene::changed, this, &TitleWidget::slotChanged); connect(font_size, SIGNAL(valueChanged(int)), m_scene, SLOT(slotUpdateFontSize(int))); connect(use_grid, &QAbstractButton::toggled, m_scene, &GraphicsSceneRectMove::slotUseGrid); // Video frame rect QPen framepen; framepen.setColor(Qt::red); m_frameBorder = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight)); m_frameBorder->setPen(framepen); m_frameBorder->setZValue(1000); m_frameBorder->setBrush(Qt::transparent); m_frameBorder->setFlags(nullptr); m_frameBorder->setData(-1, -1); graphicsView->scene()->addItem(m_frameBorder); // semi transparent safe zones framepen.setColor(QColor(255, 0, 0, 100)); QGraphicsRectItem *safe1 = new QGraphicsRectItem(QRectF(m_frameWidth * 0.05, m_frameHeight * 0.05, m_frameWidth * 0.9, m_frameHeight * 0.9), m_frameBorder); safe1->setBrush(Qt::transparent); safe1->setPen(framepen); safe1->setFlags(nullptr); safe1->setData(-1, -1); QGraphicsRectItem *safe2 = new QGraphicsRectItem(QRectF(m_frameWidth * 0.1, m_frameHeight * 0.1, m_frameWidth * 0.8, m_frameHeight * 0.8), m_frameBorder); safe2->setBrush(Qt::transparent); safe2->setPen(framepen); safe2->setFlags(nullptr); safe2->setData(-1, -1); m_frameBackground = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight)); m_frameBackground->setZValue(-1100); m_frameBackground->setBrush(Qt::transparent); m_frameBackground->setFlags(nullptr); graphicsView->scene()->addItem(m_frameBackground); m_frameImage = new QGraphicsPixmapItem(); QTransform qtrans; qtrans.scale(2.0, 2.0); m_frameImage->setTransform(qtrans); m_frameImage->setZValue(-1200); m_frameImage->setFlags(nullptr); displayBackgroundFrame(); graphicsView->scene()->addItem(m_frameImage); connect(m_scene, &QGraphicsScene::selectionChanged, this, &TitleWidget::selectionChanged); connect(m_scene, &GraphicsSceneRectMove::itemMoved, this, &TitleWidget::selectionChanged); connect(m_scene, &GraphicsSceneRectMove::sceneZoom, this, &TitleWidget::slotZoom); connect(m_scene, &GraphicsSceneRectMove::actionFinished, this, &TitleWidget::slotSelectTool); connect(m_scene, &GraphicsSceneRectMove::newRect, this, &TitleWidget::slotNewRect); connect(m_scene, &GraphicsSceneRectMove::newText, this, &TitleWidget::slotNewText); connect(zoom_slider, &QAbstractSlider::valueChanged, this, &TitleWidget::slotUpdateZoom); connect(zoom_spin, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateZoom(int))); // mbd: load saved settings loadGradients(); readChoices(); if (render->getMltVersionInfo(QStringLiteral("kdenlivetitle")) < 2.0) { // Gradients and shadows are only supported since version 2, so disable shadowBox->setEnabled(false); gradient_color->setEnabled(false); gradients_combo->setEnabled(false); gradients_rect_combo->setEnabled(false); gradient_rect->setEnabled(false); edit_gradient->setEnabled(false); edit_rect_gradient->setEnabled(false); } // Hide effects not implemented tabWidget->removeTab(3); graphicsView->show(); graphicsView->setInteractive(true); //qCDebug(KDENLIVE_LOG) << "// TITLE WIDGWT: " << graphicsView->viewport()->width() << 'x' << graphicsView->viewport()->height(); m_startViewport = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight)); // Setting data at -1 so that the item is recognized as undeletable by graphicsscenerectmove m_startViewport->setData(-1, -1); m_endViewport = new QGraphicsRectItem(QRectF(0, 0, m_frameWidth, m_frameHeight)); m_endViewport->setData(-1, -1); m_startViewport->setData(0, m_frameWidth); m_startViewport->setData(1, m_frameHeight); m_endViewport->setData(0, m_frameWidth); m_endViewport->setData(1, m_frameHeight); // scale the view so that the title widget is not too big at startup graphicsView->scale(.5, .5); if (url.isValid()) { loadTitle(url); } else { prepareTools(nullptr); slotTextTool(); QTimer::singleShot(200, this, &TitleWidget::slotAdjustZoom); } initAnimation(); QColor color = backgroundColor->color(); m_scene->setBackgroundBrush(QBrush(color)); color.setAlpha(backgroundAlpha->value()); m_frameBackground->setBrush(color); connect(anim_start, &QAbstractButton::toggled, this, &TitleWidget::slotAnimStart); connect(anim_end, &QAbstractButton::toggled, this, &TitleWidget::slotAnimEnd); connect(templateBox, SIGNAL(currentIndexChanged(int)), this, SLOT(templateIndexChanged(int))); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(KdenliveSettings::hastitleproducer()); if (titletemplates.isEmpty()) { refreshTitleTemplates(m_projectTitlePath); } //templateBox->setIconSize(QSize(60,60)); templateBox->clear(); templateBox->addItem(QString()); foreach (const TitleTemplate &t, titletemplates) { templateBox->addItem(t.icon, t.name, t.file); } lastDocumentHash = QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex(); } TitleWidget::~TitleWidget() { m_scene->blockSignals(true); delete m_buttonRect; delete m_buttonText; delete m_buttonImage; delete m_buttonCursor; delete m_buttonSave; delete m_buttonLoad; delete m_unicodeAction; delete m_zUp; delete m_zDown; delete m_zTop; delete m_zBottom; delete m_selectAll; delete m_selectText; delete m_selectRects; delete m_selectImages; delete m_unselectAll; delete m_unicodeDialog; delete m_frameBorder; delete m_frameImage; delete m_startViewport; delete m_endViewport; delete m_scene; delete m_signalMapper; } // static QStringList TitleWidget::extractImageList(const QString &xml) { QStringList result; if (xml.isEmpty()) { return result; } QDomDocument doc; doc.setContent(xml); QDomNodeList images = doc.elementsByTagName(QStringLiteral("content")); for (int i = 0; i < images.count(); ++i) { if (images.at(i).toElement().hasAttribute(QStringLiteral("url"))) { result.append(images.at(i).toElement().attribute(QStringLiteral("url"))); } } return result; } // static QStringList TitleWidget::extractFontList(const QString &xml) { QStringList result; if (xml.isEmpty()) { return result; } QDomDocument doc; doc.setContent(xml); QDomNodeList images = doc.elementsByTagName(QStringLiteral("content")); for (int i = 0; i < images.count(); ++i) { if (images.at(i).toElement().hasAttribute(QStringLiteral("font"))) { result.append(images.at(i).toElement().attribute(QStringLiteral("font"))); } } return result; } //static void TitleWidget::refreshTitleTemplates(const QString &projectPath) { QStringList filters = QStringList() << QStringLiteral("*.kdenlivetitle"); titletemplates.clear(); // project templates QDir dir(projectPath); QStringList templateFiles = dir.entryList(filters, QDir::Files); foreach (const QString &fname, templateFiles) { TitleTemplate t; t.name = fname; t.file = dir.absoluteFilePath(fname); t.icon = QIcon(KThumb::getImage(QUrl::fromLocalFile(t.file), 0, 60, 60)); titletemplates.append(t); } // system templates QStringList titleTemplates = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("titles/"), QStandardPaths::LocateDirectory); foreach (const QString &folderpath, titleTemplates) { QDir folder(folderpath); QStringList filesnames = folder.entryList(filters, QDir::Files); foreach (const QString &fname, filesnames) { TitleTemplate t; t.name = fname; t.file = folder.absoluteFilePath(fname); t.icon = QIcon(KThumb::getImage(QUrl::fromLocalFile(t.file), 0, 60, 60)); titletemplates.append(t); } } } void TitleWidget::templateIndexChanged(int index) { QString item = templateBox->itemData(index).toString(); if (!item.isEmpty()) { if (lastDocumentHash != QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex()) { if (KMessageBox::questionYesNo(this, i18n("Do you really want to load a new template? Changes in this title will be lost!")) == KMessageBox::No) { return; } } loadTitle(QUrl::fromLocalFile(item)); // mbt 1607: Add property to distinguish between unchanged template titles and user titles. // Text of unchanged template titles should be selected when clicked. QList list = graphicsView->scene()->items(); foreach (QGraphicsItem *qgItem, list) { if (qgItem->type() == TEXTITEM) { MyTextItem *i = static_cast(qgItem); i->setProperty("isTemplate", "true"); i->setProperty("templateText", i->toHtml()); } } lastDocumentHash = QCryptographicHash::hash(xml().toString().toLatin1(), QCryptographicHash::Md5).toHex(); } } //virtual void TitleWidget::resizeEvent(QResizeEvent * /*event*/) { //slotAdjustZoom(); } //virtual void TitleWidget::keyPressEvent(QKeyEvent *e) { if (e->key() != Qt::Key_Escape && e->key() != Qt::Key_Return && e->key() != Qt::Key_Enter) { QDialog::keyPressEvent(e); } } void TitleWidget::slotTextTool() { m_scene->setTool(TITLE_TEXT); showToolbars(TITLE_TEXT); checkButton(TITLE_TEXT); } void TitleWidget::slotRectTool() { m_scene->setTool(TITLE_RECTANGLE); showToolbars(TITLE_RECTANGLE); checkButton(TITLE_RECTANGLE); // Disable dragging mode, would make dragging a rect impossible otherwise ;) graphicsView->setDragMode(QGraphicsView::NoDrag); } void TitleWidget::slotSelectTool() { m_scene->setTool(TITLE_SELECT); // Enable rubberband selecting mode. graphicsView->setDragMode(QGraphicsView::RubberBandDrag); // Find out which toolbars need to be shown, depending on selected item TITLETOOL t = TITLE_SELECT; QList l = graphicsView->scene()->selectedItems(); if (!l.isEmpty()) { switch (l.at(0)->type()) { case TEXTITEM: t = TITLE_TEXT; break; case RECTITEM: t = TITLE_RECTANGLE; break; case IMAGEITEM: t = TITLE_IMAGE; break; } } enableToolbars(t); if (t == TITLE_RECTANGLE && (l.at(0) == m_endViewport || l.at(0) == m_startViewport)) { //graphicsView->centerOn(l.at(0)); t = TITLE_SELECT; } showToolbars(t); if (!l.isEmpty()) { updateCoordinates(l.at(0)); updateDimension(l.at(0)); updateRotZoom(l.at(0)); } checkButton(TITLE_SELECT); } void TitleWidget::slotImageTool() { QList supported = QImageReader::supportedImageFormats(); QStringList mimeTypeFilters; QString allExtensions = i18n("All Images") + QStringLiteral(" ("); foreach (const QByteArray &mimeType, supported) { mimeTypeFilters.append(i18n("%1 Image", QString(mimeType)) + QStringLiteral("( *.") + QString(mimeType) + QLatin1Char(')')); allExtensions.append(QStringLiteral("*.") + mimeType + QLatin1Char(' ')); } mimeTypeFilters.sort(); allExtensions.append(QLatin1Char(')')); mimeTypeFilters.prepend(allExtensions); QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveImageFolder")); if (clipFolder.isEmpty()) { clipFolder = QDir::homePath(); } QFileDialog dialog(this, i18n("Add Image"), clipFolder); dialog.setAcceptMode(QFileDialog::AcceptOpen); dialog.setNameFilters(mimeTypeFilters); if (dialog.exec() != QDialog::Accepted) { return; } QUrl url = QUrl::fromLocalFile(dialog.selectedFiles().at(0)); if (url.isValid()) { KRecentDirs::add(QStringLiteral(":KdenliveImageFolder"), url.adjusted(QUrl::RemoveFilename).toLocalFile()); if (url.toLocalFile().endsWith(QLatin1String(".svg"))) { MySvgItem *svg = new MySvgItem(url.toLocalFile()); svg->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges); svg->setZValue(m_count++); svg->setData(Qt::UserRole, url.toLocalFile()); m_scene->addNewItem(svg); prepareTools(svg); } else { QPixmap pix(url.toLocalFile()); MyPixmapItem *image = new MyPixmapItem(pix); image->setShapeMode(QGraphicsPixmapItem::BoundingRectShape); image->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges); image->setData(Qt::UserRole, url.toLocalFile()); image->setZValue(m_count++); m_scene->addNewItem(image); prepareTools(image); } } m_scene->setTool(TITLE_SELECT); showToolbars(TITLE_SELECT); checkButton(TITLE_SELECT); } void TitleWidget::showToolbars(TITLETOOL toolType) { //toolbar_stack->setEnabled(toolType != TITLE_SELECT); switch (toolType) { case TITLE_IMAGE: toolbar_stack->setCurrentIndex(2); break; case TITLE_RECTANGLE: toolbar_stack->setCurrentIndex(1); break; case TITLE_TEXT: default: toolbar_stack->setCurrentIndex(0); break; } } void TitleWidget::enableToolbars(TITLETOOL toolType) { // TITLETOOL is defined in effectstack/graphicsscenerectmove.h bool enable = false; if (toolType == TITLE_RECTANGLE || toolType == TITLE_IMAGE) { enable = true; } value_w->setEnabled(enable); value_h->setEnabled(enable); } void TitleWidget::checkButton(TITLETOOL toolType) { bool bSelect = false; bool bText = false; bool bRect = false; bool bImage = false; switch (toolType) { case TITLE_SELECT: bSelect = true; break; case TITLE_TEXT: bText = true; break; case TITLE_RECTANGLE: bRect = true; break; case TITLE_IMAGE: bImage = true; break; default: break; } m_buttonCursor->setChecked(bSelect); m_buttonText->setChecked(bText); m_buttonRect->setChecked(bRect); m_buttonImage->setChecked(bImage); } void TitleWidget::displayBackgroundFrame() { QRectF r = m_frameBorder->sceneBoundingRect(); if (!displayBg->isChecked()) { QPixmap pattern(20, 20); pattern.fill(); QColor bgcolor(210, 210, 210); QPainter p(&pattern); p.fillRect(QRect(0, 0, 10, 10), bgcolor); p.fillRect(QRect(10, 10, 20, 20), bgcolor); p.end(); QBrush br(pattern); QPixmap bg((int)(r.width() / 2), (int)(r.height() / 2)); QPainter p2(&bg); p2.fillRect(bg.rect(), br); p2.end(); m_frameImage->setPixmap(bg); } else { emit requestBackgroundFrame(m_clipId, true); } } void TitleWidget::slotGotBackground(const QImage &img) { QRectF r = m_frameBorder->sceneBoundingRect(); m_frameImage->setPixmap(QPixmap::fromImage(img.scaled(r.width() / 2, r.height() / 2))); emit requestBackgroundFrame(m_clipId, false); } void TitleWidget::initAnimation() { align_box->setEnabled(false); QPen startpen(Qt::DotLine); QPen endpen(Qt::DashDotLine); startpen.setColor(QColor(100, 200, 100, 140)); endpen.setColor(QColor(200, 100, 100, 140)); m_startViewport->setPen(startpen); m_endViewport->setPen(endpen); m_startViewport->setZValue(-1000); m_endViewport->setZValue(-1000); m_startViewport->setFlags(nullptr); m_endViewport->setFlags(nullptr); graphicsView->scene()->addItem(m_startViewport); graphicsView->scene()->addItem(m_endViewport); connect(keep_aspect, &QAbstractButton::toggled, this, &TitleWidget::slotKeepAspect); connect(resize50, &QAbstractButton::clicked, this, &TitleWidget::slotResize50); connect(resize100, &QAbstractButton::clicked, this, &TitleWidget::slotResize100); connect(resize200, &QAbstractButton::clicked, this, &TitleWidget::slotResize200); } void TitleWidget::slotUpdateZoom(int pos) { zoom_spin->setValue(pos); zoom_slider->setValue(pos); m_scene->setZoom((double) pos / 100); } void TitleWidget::slotZoom(bool up) { int pos = zoom_slider->value(); if (up) { pos++; } else { pos--; } zoom_slider->setValue(pos); } void TitleWidget::slotAdjustZoom() { /*double scalex = graphicsView->width() / (double)(m_frameWidth * 1.2); double scaley = graphicsView->height() / (double)(m_frameHeight * 1.2); if (scalex > scaley) scalex = scaley; int zoompos = (int)(scalex * 7 + 0.5);*/ graphicsView->fitInView(m_frameBorder, Qt::KeepAspectRatio); int zoompos = graphicsView->matrix().m11() * 100; zoom_slider->setValue(zoompos); graphicsView->centerOn(m_frameBorder); } void TitleWidget::slotZoomOneToOne() { zoom_slider->setValue(100); graphicsView->centerOn(m_frameBorder); } void TitleWidget::slotNewRect(QGraphicsRectItem *rect) { updateAxisButtons(rect); // back to default if (rectLineWidth->value() == 0) { rect->setPen(Qt::NoPen); } else { QPen penf(rectFColor->color()); penf.setWidth(rectLineWidth->value()); penf.setJoinStyle(Qt::RoundJoin); rect->setPen(penf); } if (plain_rect->isChecked()) { rect->setBrush(QBrush(rectBColor->color())); rect->setData(TitleDocument::Gradient, QVariant()); } else { // gradient QString gradientData = gradients_rect_combo->currentData().toString(); rect->setData(TitleDocument::Gradient, gradientData); QLinearGradient gr = GradientWidget::gradientFromString(gradientData, rect->boundingRect().width(), rect->boundingRect().height()); rect->setBrush(QBrush(gr)); } rect->setZValue(m_count++); rect->setData(TitleDocument::ZoomFactor, 100); prepareTools(rect); //setCurrentItem(rect); //graphicsView->setFocus(); } void TitleWidget::slotNewText(MyTextItem *tt) { updateAxisButtons(tt); // back to default letter_spacing->blockSignals(true); line_spacing->blockSignals(true); letter_spacing->setValue(0); line_spacing->setValue(0); letter_spacing->blockSignals(false); line_spacing->blockSignals(false); letter_spacing->setEnabled(true); line_spacing->setEnabled(true); QFont font = font_family->currentFont(); font.setPixelSize(font_size->value()); // mbd: issue 551: font.setWeight(font_weight_box->itemData(font_weight_box->currentIndex()).toInt()); font.setItalic(buttonItalic->isChecked()); font.setUnderline(buttonUnder->isChecked()); tt->setFont(font); QColor color = fontColorButton->color(); QColor outlineColor = textOutlineColor->color(); tt->setTextColor(color); tt->document()->setDocumentMargin(0); QTextCursor cur(tt->document()); cur.select(QTextCursor::Document); QTextBlockFormat format = cur.blockFormat(); QTextCharFormat cformat = cur.charFormat(); double outlineWidth = textOutline->value() / 10.0; tt->setData(TitleDocument::OutlineWidth, outlineWidth); tt->setData(TitleDocument::OutlineColor, outlineColor); if (outlineWidth > 0.0) { cformat.setTextOutline(QPen(outlineColor, outlineWidth)); } tt->updateShadow(shadowBox->isChecked(), blur_radius->value(), shadowX->value(), shadowY->value(), shadowColor->color()); if (gradient_color->isChecked()) { QString gradientData = gradients_combo->currentData().toString(); tt->setData(TitleDocument::Gradient, gradientData); QLinearGradient gr = GradientWidget::gradientFromString(gradientData, tt->boundingRect().width(), tt->boundingRect().height()); cformat.setForeground(QBrush(gr)); } else { cformat.setForeground(QBrush(color)); } cur.setCharFormat(cformat); cur.setBlockFormat(format); tt->setTextCursor(cur); tt->setZValue(m_count++); setCurrentItem(tt); prepareTools(tt); } void TitleWidget::setFontBoxWeight(int weight) { int index = font_weight_box->findData(weight); if (index < 0) { index = font_weight_box->findData(QFont::Normal); } font_weight_box->setCurrentIndex(index); } void TitleWidget::setCurrentItem(QGraphicsItem *item) { m_scene->setSelectedItem(item); } void TitleWidget::zIndexChanged(int v) { QList l = graphicsView->scene()->selectedItems(); for (int i = 0; i < l.size(); ++i) { l[i]->setZValue(v); } } void TitleWidget::selectionChanged() { if (m_scene->tool() != TITLE_SELECT) { return; } //qCDebug(KDENLIVE_LOG) << "Number of selected items: " << graphicsView->scene()->selectedItems().length() << '\n'; QList l; // mbt 1607: One text item might have grabbed the keyboard. // Ungrab it for all items that are not selected, otherwise // text input would only work for the text item that grabbed // the keyboard last. l = graphicsView->scene()->items(); foreach (QGraphicsItem *item, l) { if (item->type() == TEXTITEM && !item->isSelected()) { MyTextItem *i = static_cast(item); i->clearFocus(); } } l = graphicsView->scene()->selectedItems(); if (!l.isEmpty()) { buttonUnselectAll->setEnabled(true); // Enable all z index buttons if items selected. // We can selectively disable them later. zUp->setEnabled(true); zDown->setEnabled(true); zTop->setEnabled(true); zBottom->setEnabled(true); } else { buttonUnselectAll->setEnabled(false); } if (l.size() >= 2) { buttonSelectText->setEnabled(true); buttonSelectRects->setEnabled(true); buttonSelectImages->setEnabled(true); } else { buttonSelectText->setEnabled(false); buttonSelectRects->setEnabled(false); buttonSelectImages->setEnabled(false); } if (l.size() == 0) { prepareTools(nullptr); } else if (l.size() == 1) { prepareTools(l.at(0)); } else { /* For multiple selected objects we need to decide which tools to show. */ int firstType = l.at(0)->type(); bool allEqual = true; for (int i = 0; i < l.size(); ++i) { if (l.at(i)->type() != firstType) { allEqual = false; break; } } //qCDebug(KDENLIVE_LOG) << "All equal? " << allEqual << ".\n"; if (allEqual) { prepareTools(l.at(0)); } else { // Get the default toolset, but enable the property frame (x,y,w,h) prepareTools(nullptr); frame_properties->setEnabled(true); // Enable x/y/w/h if it makes sense. value_x->setEnabled(true); value_y->setEnabled(true); bool containsTextitem = false; for (int i = 0; i < l.size(); ++i) { if (l.at(i)->type() == TEXTITEM) { containsTextitem = true; break; } } if (!containsTextitem) { value_w->setEnabled(true); value_h->setEnabled(true); } } // Disable z index buttons if they don't make sense for the current selection int firstZindex = l.at(0)->zValue(); allEqual = true; for (int i = 0; i < l.size(); ++i) { if (l[i]->zValue() != firstZindex) { allEqual = false; break; } } if (!allEqual) { zUp->setEnabled(false); zDown->setEnabled(false); } } } void TitleWidget::slotValueChanged(int type) { /* type tells us which QSpinBox value has changed. */ QList l = graphicsView->scene()->selectedItems(); //qCDebug(KDENLIVE_LOG) << l.size() << " items to be resized\n"; // Get the updated value here already to do less coding afterwards int val = 0; switch (type) { case ValueWidth: val = value_w->value(); break; case ValueHeight: val = value_h->value(); break; case ValueX: val = value_x->value(); break; case ValueY: val = value_y->value(); break; } for (int k = 0; k < l.size(); ++k) { //qCDebug(KDENLIVE_LOG) << "Type of item " << k << ": " << l.at(k)->type() << '\n'; if (l.at(k)->type() == TEXTITEM) { // Just update the position. We don't allow setting width/height for text items yet. switch (type) { case ValueX: updatePosition(l.at(k), val, l.at(k)->pos().y()); break; case ValueY: updatePosition(l.at(k), l.at(k)->pos().x(), val); break; } } else if (l.at(k)->type() == RECTITEM) { QGraphicsRectItem *rec = static_cast (l.at(k)); switch (type) { case ValueX: updatePosition(l.at(k), val, l.at(k)->pos().y()); break; case ValueY: updatePosition(l.at(k), l.at(k)->pos().x(), val); break; case ValueWidth: rec->setRect(QRect(0, 0, val, rec->rect().height())); break; case ValueHeight: rec->setRect(QRect(0, 0, rec->rect().width(), val)); break; } } else if (l.at(k)->type() == IMAGEITEM) { if (type == ValueX) { updatePosition(l.at(k), val, l.at(k)->pos().y()); } else if (type == ValueY) { updatePosition(l.at(k), l.at(k)->pos().x(), val); } else { // Width/height has changed. This is more complex. QGraphicsItem *i = l.at(k); Transform t = m_transformations.value(i); // Ratio width:height double phi = (double) i->boundingRect().width() / i->boundingRect().height(); // TODO: proper calculation for rotation around 3 axes double alpha = (double) t.rotatez / 180.0 * M_PI; // New length double length; // Scaling factor double scale = 1; // We want to keep the aspect ratio of the image as the user does not yet have the possibility // to restore the original ratio. You rarely want to change it anyway. switch (type) { case ValueWidth: // Add 0.5 because otherwise incrementing by 1 might have no effect length = val / (cos(alpha) + 1 / phi * sin(alpha)) + 0.5; scale = length / i->boundingRect().width(); break; case ValueHeight: length = val / (phi * sin(alpha) + cos(alpha)) + 0.5; scale = length / i->boundingRect().height(); break; } t.scalex = scale; t.scaley = scale; QTransform qtrans; qtrans.scale(scale, scale); qtrans.rotate(t.rotatex, Qt::XAxis); qtrans.rotate(t.rotatey, Qt::YAxis); qtrans.rotate(t.rotatez, Qt::ZAxis); i->setTransform(qtrans); //qCDebug(KDENLIVE_LOG) << "scale is: " << scale << '\n'; //qCDebug(KDENLIVE_LOG) << i->boundingRect().width() << ": new width\n"; m_transformations[i] = t; if (l.size() == 1) { // Only update the w/h values if the selection contains just one item. // Otherwise, what should we do? ;) // (Use the values of the first item? Of the second? Of the x-th?) updateDimension(i); // Update rotation/zoom values. // These values are not yet able to handle multiple items! updateRotZoom(i); } } } } } void TitleWidget::updateDimension(QGraphicsItem *i) { bool wBlocked = value_w->signalsBlocked(); bool hBlocked = value_h->signalsBlocked(); bool zBlocked = zValue->signalsBlocked(); value_w->blockSignals(true); value_h->blockSignals(true); zValue->blockSignals(true); zValue->setValue((int) i->zValue()); if (i->type() == IMAGEITEM) { // Get multipliers for rotation/scaling /*Transform t = m_transformations.value(i); QRectF r = i->boundingRect(); int width = (int) ( abs(r.width()*t.scalex * cos(t.rotate/180.0*M_PI)) + abs(r.height()*t.scaley * sin(t.rotate/180.0*M_PI)) ); int height = (int) ( abs(r.height()*t.scaley * cos(t.rotate/180*M_PI)) + abs(r.width()*t.scalex * sin(t.rotate/180*M_PI)) );*/ value_w->setValue(i->sceneBoundingRect().width()); value_h->setValue(i->sceneBoundingRect().height()); } else if (i->type() == RECTITEM) { QGraphicsRectItem *r = static_cast (i); //qCDebug(KDENLIVE_LOG) << "Rect width is: " << r->rect().width() << ", was: " << value_w->value() << '\n'; value_w->setValue((int) r->rect().width()); value_h->setValue((int) r->rect().height()); } else if (i->type() == TEXTITEM) { MyTextItem *t = static_cast (i); value_w->setValue((int) t->boundingRect().width()); value_h->setValue((int) t->boundingRect().height()); } zValue->blockSignals(zBlocked); value_w->blockSignals(wBlocked); value_h->blockSignals(hBlocked); } void TitleWidget::updateCoordinates(QGraphicsItem *i) { // Block signals emitted by this method value_x->blockSignals(true); value_y->blockSignals(true); if (i->type() == TEXTITEM) { MyTextItem *rec = static_cast (i); // Set the correct x coordinate value if (origin_x_left->isChecked()) { // Origin (0 point) is at m_frameWidth, coordinate axis is inverted value_x->setValue((int)(m_frameWidth - rec->pos().x() - rec->boundingRect().width())); } else { // Origin is at 0 (default) value_x->setValue((int) rec->pos().x()); } // Same for y if (origin_y_top->isChecked()) { value_y->setValue((int)(m_frameHeight - rec->pos().y() - rec->boundingRect().height())); } else { value_y->setValue((int) rec->pos().y()); } } else if (i->type() == RECTITEM) { QGraphicsRectItem *rec = static_cast (i); if (origin_x_left->isChecked()) { // Origin (0 point) is at m_frameWidth value_x->setValue((int)(m_frameWidth - rec->pos().x() - rec->rect().width())); } else { // Origin is at 0 (default) value_x->setValue((int) rec->pos().x()); } if (origin_y_top->isChecked()) { value_y->setValue((int)(m_frameHeight - rec->pos().y() - rec->rect().height())); } else { value_y->setValue((int) rec->pos().y()); } } else if (i->type() == IMAGEITEM) { if (origin_x_left->isChecked()) { value_x->setValue((int)(m_frameWidth - i->pos().x() - i->sceneBoundingRect().width())); } else { value_x->setValue((int) i->pos().x()); } if (origin_y_top->isChecked()) { value_y->setValue((int)(m_frameHeight - i->pos().y() - i->sceneBoundingRect().height())); } else { value_y->setValue((int) i->pos().y()); } } // Stop blocking signals now value_x->blockSignals(false); value_y->blockSignals(false); } void TitleWidget::updateRotZoom(QGraphicsItem *i) { itemzoom->blockSignals(true); itemrotatex->blockSignals(true); itemrotatey->blockSignals(true); itemrotatez->blockSignals(true); Transform t = m_transformations.value(i); if (!i->data(TitleDocument::ZoomFactor).isNull()) { itemzoom->setValue(i->data(TitleDocument::ZoomFactor).toInt()); } else { itemzoom->setValue((int)(t.scalex * 100.0 + 0.5)); } itemrotatex->setValue((int)(t.rotatex)); itemrotatey->setValue((int)(t.rotatey)); itemrotatez->setValue((int)(t.rotatez)); itemzoom->blockSignals(false); itemrotatex->blockSignals(false); itemrotatey->blockSignals(false); itemrotatez->blockSignals(false); } void TitleWidget::updatePosition(QGraphicsItem *i) { updatePosition(i, value_x->value(), value_y->value()); } void TitleWidget::updatePosition(QGraphicsItem *i, int x, int y) { if (i->type() == TEXTITEM) { MyTextItem *rec = static_cast (i); int posX; if (origin_x_left->isChecked()) { /* * Origin of the X axis is at m_frameWidth, and distance from right * border of the item to the right border of the frame is taken. See * comment to slotOriginXClicked(). */ posX = m_frameWidth - x - rec->boundingRect().width(); } else { posX = x; } int posY; if (origin_y_top->isChecked()) { /* Same for y axis */ posY = m_frameHeight - y - rec->boundingRect().height(); } else { posY = y; } rec->setPos(posX, posY); } else if (i->type() == RECTITEM) { QGraphicsRectItem *rec = static_cast (i); int posX; if (origin_x_left->isChecked()) { posX = m_frameWidth - x - rec->rect().width(); } else { posX = x; } int posY; if (origin_y_top->isChecked()) { posY = m_frameHeight - y - rec->rect().height(); } else { posY = y; } rec->setPos(posX, posY); } else if (i->type() == IMAGEITEM) { int posX; if (origin_x_left->isChecked()) { // Use the sceneBoundingRect because this also regards transformations like zoom posX = m_frameWidth - x - i->sceneBoundingRect().width(); } else { posX = x; } int posY; if (origin_y_top->isChecked()) { posY = m_frameHeight - y - i->sceneBoundingRect().height(); } else { posY = y; } i->setPos(posX, posY); } } void TitleWidget::updateTextOriginX() { if (origin_x_left->isChecked()) { origin_x_left->setText(i18n("\u2212X")); } else { origin_x_left->setText(i18n("+X")); } } void TitleWidget::slotOriginXClicked() { // Update the text displayed on the button. updateTextOriginX(); QList l = graphicsView->scene()->selectedItems(); if (l.size() >= 1) { updateCoordinates(l.at(0)); // Remember x axis setting l.at(0)->setData(TitleDocument::OriginXLeft, origin_x_left->isChecked() ? TitleDocument::AxisInverted : TitleDocument::AxisDefault); } graphicsView->setFocus(); } void TitleWidget::updateTextOriginY() { if (origin_y_top->isChecked()) { origin_y_top->setText(i18n("\u2212Y")); } else { origin_y_top->setText(i18n("+Y")); } } void TitleWidget::slotOriginYClicked() { // Update the text displayed on the button. updateTextOriginY(); QList l = graphicsView->scene()->selectedItems(); if (l.size() >= 1) { updateCoordinates(l.at(0)); l.at(0)->setData(TitleDocument::OriginYTop, origin_y_top->isChecked() ? TitleDocument::AxisInverted : TitleDocument::AxisDefault); } graphicsView->setFocus(); } void TitleWidget::updateAxisButtons(QGraphicsItem *i) { int xAxis = i->data(TitleDocument::OriginXLeft).toInt(); int yAxis = i->data(TitleDocument::OriginYTop).toInt(); origin_x_left->blockSignals(true); origin_y_top->blockSignals(true); if (xAxis == TitleDocument::AxisInverted) { origin_x_left->setChecked(true); } else { origin_x_left->setChecked(false); } updateTextOriginX(); if (yAxis == TitleDocument::AxisInverted) { origin_y_top->setChecked(true); } else { origin_y_top->setChecked(false); } updateTextOriginY(); origin_x_left->blockSignals(false); origin_y_top->blockSignals(false); } void TitleWidget::slotChangeBackground() { QColor color = backgroundColor->color(); m_scene->setBackgroundBrush(QBrush(color)); color.setAlpha(backgroundAlpha->value()); m_frameBackground->setBrush(QBrush(color)); } void TitleWidget::slotChanged() { QList l = graphicsView->scene()->selectedItems(); if (l.size() >= 1 && l.at(0)->type() == TEXTITEM) { textChanged(static_cast (l.at(0))); } } void TitleWidget::textChanged(MyTextItem *i) { /* * If the user has set origin_x_left (the same for y), we need to look * whether a text element has been selected. If yes, we need to ensure that * the right border of the text field remains fixed also when some text has * been entered. * * This is also known as right-justified, with the difference that it is not * valid for text but for its boundingRect. Text may still be * left-justified. */ updateDimension(i); if (origin_x_left->isChecked() || origin_y_top->isChecked()) { if (!i->document()->isEmpty()) { updatePosition(i); } else { /* * Don't do anything if the string is empty. If the position were * updated here, a newly created text field would be set to the * position of the last selected text field. */ } } // mbt 1607: Template text has changed; don't auto-select content anymore. if (i->property("isTemplate").isValid()) { if (i->property("templateText").isValid()) { if (i->property("templateText") == i->toHtml()) { // Unchanged, do nothing. } else { i->setProperty("isTemplate", QVariant::Invalid); i->setProperty("templateText", QVariant::Invalid); } } } } void TitleWidget::slotInsertUnicode() { m_unicodeDialog->exec(); } void TitleWidget::slotInsertUnicodeString(const QString &text) { const QList l = graphicsView->scene()->selectedItems(); if (!l.isEmpty()) { if (l.at(0)->type() == TEXTITEM) { MyTextItem *t = static_cast (l.at(0)); t->textCursor().insertText(text); } } } void TitleWidget::slotUpdateText() { QFont font = font_family->currentFont(); font.setPixelSize(font_size->value()); font.setItalic(buttonItalic->isChecked()); font.setUnderline(buttonUnder->isChecked()); font.setWeight(font_weight_box->itemData(font_weight_box->currentIndex()).toInt()); font.setLetterSpacing(QFont::AbsoluteSpacing, letter_spacing->value()); QColor color = fontColorButton->color(); QColor outlineColor = textOutlineColor->color(); QString gradientData; if (gradient_color->isChecked()) { // user wants a gradient gradientData = gradients_combo->currentData().toString(); } double outlineWidth = textOutline->value() / 10.0; int i; QList l = graphicsView->scene()->selectedItems(); for (i = 0; i < l.length(); ++i) { MyTextItem *item = nullptr; if (l.at(i)->type() == TEXTITEM) { item = static_cast (l.at(i)); } if (!item) { // No text item, try next one. continue; } // Set alignment of all text in the text item QTextCursor cur(item->document()); cur.select(QTextCursor::Document); QTextBlockFormat format = cur.blockFormat(); item->setData(TitleDocument::LineSpacing, line_spacing->value()); format.setLineHeight(line_spacing->value(), QTextBlockFormat::LineDistanceHeight); if (buttonAlignLeft->isChecked() || buttonAlignCenter->isChecked() || buttonAlignRight->isChecked()) { if (buttonAlignCenter->isChecked()) { item->setAlignment(Qt::AlignHCenter); } else if (buttonAlignRight->isChecked()) { item->setAlignment(Qt::AlignRight); } else if (buttonAlignLeft->isChecked()) { item->setAlignment(Qt::AlignLeft); } } else { item->setAlignment(Qt::AlignLeft); } // Set font properties item->setFont(font); QTextCharFormat cformat = cur.charFormat(); item->setData(TitleDocument::OutlineWidth, outlineWidth); item->setData(TitleDocument::OutlineColor, outlineColor); if (outlineWidth > 0.0) { cformat.setTextOutline(QPen(outlineColor, outlineWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); } if (gradientData.isEmpty()) { cformat.setForeground(QBrush(color)); } else { QLinearGradient gr = GradientWidget::gradientFromString(gradientData, item->boundingRect().width(), item->boundingRect().height()); cformat.setForeground(QBrush(gr)); } // Store gradient in item properties item->setData(TitleDocument::Gradient, gradientData); cur.setCharFormat(cformat); cur.setBlockFormat(format); // item->setTextCursor(cur); cur.clearSelection(); item->setTextCursor(cur); item->setTextColor(color); } } void TitleWidget::rectChanged() { QList l = graphicsView->scene()->selectedItems(); for (int i = 0; i < l.length(); ++i) { if (l.at(i)->type() == RECTITEM && !settingUp) { QGraphicsRectItem *rec = static_cast(l.at(i)); QColor f = rectFColor->color(); if (rectLineWidth->value() == 0) { rec->setPen(Qt::NoPen); } else { QPen penf(f); penf.setWidth(rectLineWidth->value()); penf.setJoinStyle(Qt::RoundJoin); rec->setPen(penf); } if (plain_rect->isChecked()) { rec->setBrush(QBrush(rectBColor->color())); rec->setData(TitleDocument::Gradient, QVariant()); } else { // gradient QString gradientData = gradients_rect_combo->currentData().toString(); rec->setData(TitleDocument::Gradient, gradientData); QLinearGradient gr = GradientWidget::gradientFromString(gradientData, rec->boundingRect().width(), rec->boundingRect().height()); rec->setBrush(QBrush(gr)); } } } } void TitleWidget::itemScaled(int val) { QList l = graphicsView->scene()->selectedItems(); if (l.size() == 1) { Transform x = m_transformations.value(l.at(0)); x.scalex = (double)val / 100.0; x.scaley = (double)val / 100.0; QTransform qtrans; qtrans.scale(x.scalex, x.scaley); qtrans.rotate(x.rotatex, Qt::XAxis); qtrans.rotate(x.rotatey, Qt::YAxis); qtrans.rotate(x.rotatez, Qt::ZAxis); l[0]->setTransform(qtrans); l[0]->setData(TitleDocument::ZoomFactor, val); m_transformations[l.at(0)] = x; updateDimension(l.at(0)); } } void TitleWidget::itemRotateX(int val) { itemRotate(val, 0); } void TitleWidget::itemRotateY(int val) { itemRotate(val, 1); } void TitleWidget::itemRotateZ(int val) { itemRotate(val, 2); } void TitleWidget::itemRotate(int val, int axis) { QList l = graphicsView->scene()->selectedItems(); if (l.size() == 1) { Transform x = m_transformations[l.at(0)]; switch (axis) { case 0: x.rotatex = val; break; case 1: x.rotatey = val; break; case 2: x.rotatez = val; break; } l[0]->setData(TitleDocument::RotateFactor, QList() << QVariant(x.rotatex) << QVariant(x.rotatey) << QVariant(x.rotatez)); QTransform qtrans; qtrans.scale(x.scalex, x.scaley); qtrans.rotate(x.rotatex, Qt::XAxis); qtrans.rotate(x.rotatey, Qt::YAxis); qtrans.rotate(x.rotatez, Qt::ZAxis); l[0]->setTransform(qtrans); m_transformations[l.at(0)] = x; if (l[0]->data(TitleDocument::ZoomFactor).isNull()) { l[0]->setData(TitleDocument::ZoomFactor, 100); } updateDimension(l.at(0)); } } void TitleWidget::itemHCenter() { QList l = graphicsView->scene()->selectedItems(); if (l.size() == 1) { QGraphicsItem *item = l.at(0); QRectF br = item->sceneBoundingRect(); int width = (int)br.width(); int newPos = (int)((m_frameWidth - width) / 2); newPos += item->pos().x() - br.left(); // Check item transformation item->setPos(newPos, item->pos().y()); updateCoordinates(item); } } void TitleWidget::itemVCenter() { QList l = graphicsView->scene()->selectedItems(); if (l.size() == 1) { QGraphicsItem *item = l.at(0); QRectF br = item->sceneBoundingRect(); int height = (int)br.height(); int newPos = (int)((m_frameHeight - height) / 2); newPos += item->pos().y() - br.top(); // Check item transformation item->setPos(item->pos().x(), newPos); updateCoordinates(item); } } void TitleWidget::itemTop() { QList l = graphicsView->scene()->selectedItems(); if (l.size() == 1) { QGraphicsItem *item = l.at(0); QRectF br = item->sceneBoundingRect(); double diff; if (br.top() > 0) { diff = -br.top(); } else { diff = -br.bottom(); } item->moveBy(0, diff); updateCoordinates(item); } } void TitleWidget::itemBottom() { QList l = graphicsView->scene()->selectedItems(); if (l.size() == 1) { QGraphicsItem *item = l.at(0); QRectF br = item->sceneBoundingRect(); double diff; if (br.bottom() > m_frameHeight) { diff = m_frameHeight - br.top(); } else { diff = m_frameHeight - br.bottom(); } item->moveBy(0, diff); updateCoordinates(item); } } void TitleWidget::itemLeft() { QList l = graphicsView->scene()->selectedItems(); if (l.size() == 1) { QGraphicsItem *item = l.at(0); QRectF br = item->sceneBoundingRect(); double diff; if (br.left() > 0) { diff = -br.left(); } else { diff = -br.right(); } item->moveBy(diff, 0); updateCoordinates(item); } } void TitleWidget::itemRight() { QList l = graphicsView->scene()->selectedItems(); if (l.size() == 1) { QGraphicsItem *item = l.at(0); QRectF br = item->sceneBoundingRect(); double diff; if (br.right() < m_frameWidth) { diff = m_frameWidth - br.right(); } else { diff = m_frameWidth - br.left(); } item->moveBy(diff, 0); updateCoordinates(item); } } void TitleWidget::loadTitle(QUrl url) { if (!url.isValid()) { url = QFileDialog::getOpenFileUrl(this, i18n("Load Title"), QUrl::fromLocalFile(m_projectTitlePath), i18n("Kdenlive title (*.kdenlivetitle)")); } if (url.isValid()) { QList items = m_scene->items(); items.removeAll(m_frameBorder); items.removeAll(m_frameBackground); items.removeAll(m_frameImage); for (int i = 0; i < items.size(); ++i) { if (items.at(i)->zValue() > -1000) { delete items.at(i); } } m_scene->clearTextSelection(); QDomDocument doc; QFile file(url.toLocalFile()); doc.setContent(&file, false); file.close(); setXml(doc); } } void TitleWidget::saveTitle(QUrl url) { if (anim_start->isChecked()) { slotAnimStart(false); } if (anim_end->isChecked()) { slotAnimEnd(false); } bool embed_image = false; // If we have images in the title, ask for embed QList list = graphicsView->scene()->items(); QGraphicsPixmapItem pix; int pixmapType = pix.type(); foreach (const QGraphicsItem *item, list) { if (item->type() == pixmapType && item != m_frameImage) { embed_image = true; break; } } if (embed_image && KMessageBox::questionYesNo(this, i18n("Do you want to embed Images into this TitleDocument?\nThis is most needed for sharing Titles.")) != KMessageBox::Yes) { embed_image = false; } if (!url.isValid()) { QPointer fs = new QFileDialog(this, i18n("Save Title"), m_projectTitlePath); fs->setMimeTypeFilters(QStringList() << QStringLiteral("application/x-kdenlivetitle")); fs->setFileMode(QFileDialog::AnyFile); fs->setAcceptMode(QFileDialog::AcceptSave); fs->setDefaultSuffix(QStringLiteral("kdenlivetitle")); //TODO: KF5 porting? //fs->setConfirmOverwrite(true); //fs->setKeepLocation(true); if (fs->exec() && !fs->selectedUrls().isEmpty()) { url = fs->selectedUrls().constFirst(); } delete fs; } if (url.isValid()) { if (m_titledocument.saveDocument(url, m_startViewport, m_endViewport, m_tc.getFrameCount(title_duration->text()), embed_image) == false) { KMessageBox::error(this, i18n("Cannot write to file %1", url.toLocalFile())); } } } QDomDocument TitleWidget::xml() { QDomDocument doc = m_titledocument.xml(m_startViewport, m_endViewport); doc.documentElement().setAttribute(QStringLiteral("duration"), m_tc.getFrameCount(title_duration->text())); doc.documentElement().setAttribute(QStringLiteral("out"), m_tc.getFrameCount(title_duration->text())); return doc; } int TitleWidget::duration() const { return m_tc.getFrameCount(title_duration->text()); } void TitleWidget::setXml(const QDomDocument &doc, const QString &id) { m_clipId = id; int duration; m_count = m_titledocument.loadFromXml(doc, m_startViewport, m_endViewport, &duration, m_projectTitlePath); adjustFrameSize(); title_duration->setText(m_tc.getTimecode(GenTime(duration, m_fps))); /*if (doc.documentElement().hasAttribute("out")) { GenTime duration = GenTime(doc.documentElement().attribute("out").toDouble() / 1000.0); title_duration->setText(m_tc.getTimecode(duration)); } else title_duration->setText(m_tc.getTimecode(GenTime(5000)));*/ QDomElement e = doc.documentElement(); m_transformations.clear(); QList items = graphicsView->scene()->items(); const double PI = 4.0 * atan(1.0); for (int i = 0; i < items.count(); ++i) { QTransform t = items.at(i)->transform(); Transform x; x.scalex = t.m11(); x.scaley = t.m22(); if (!items.at(i)->data(TitleDocument::RotateFactor).isNull()) { QList rotlist = items.at(i)->data(TitleDocument::RotateFactor).toList(); if (rotlist.count() >= 3) { x.rotatex = rotlist[0].toDouble(); x.rotatey = rotlist[1].toDouble(); x.rotatez = rotlist[2].toDouble(); // Try to adjust zoom t.rotate(x.rotatex * (-1), Qt::XAxis); t.rotate(x.rotatey * (-1), Qt::YAxis); t.rotate(x.rotatez * (-1), Qt::ZAxis); x.scalex = t.m11(); x.scaley = t.m22(); } else { x.rotatex = 0; x.rotatey = 0; x.rotatez = 0; } } else { x.rotatex = 0; x.rotatey = 0; x.rotatez = 180. / PI * atan2(-t.m21(), t.m11()); } m_transformations[items.at(i)] = x; } // mbd: Update the GUI color selectors to match the stuff from the loaded document QColor background_color = m_titledocument.getBackgroundColor(); backgroundAlpha->blockSignals(true); backgroundColor->blockSignals(true); backgroundAlpha->setValue(background_color.alpha()); background_color.setAlpha(255); backgroundColor->setColor(background_color); backgroundAlpha->blockSignals(false); backgroundColor->blockSignals(false); /*startViewportX->setValue(m_startViewport->data(0).toInt()); startViewportY->setValue(m_startViewport->data(1).toInt()); startViewportSize->setValue(m_startViewport->data(2).toInt()); endViewportX->setValue(m_endViewport->data(0).toInt()); endViewportY->setValue(m_endViewport->data(1).toInt()); endViewportSize->setValue(m_endViewport->data(2).toInt());*/ QTimer::singleShot(200, this, &TitleWidget::slotAdjustZoom); slotSelectTool(); selectionChanged(); } void TitleWidget::slotAccepted() { if (anim_start->isChecked()) { slotAnimStart(false); } if (anim_end->isChecked()) { slotAnimEnd(false); } writeChoices(); } void TitleWidget::writeChoices() { // Get a pointer to a shared configuration instance, then get the TitleWidget group. KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup titleConfig(config, "TitleWidget"); // Write the entries titleConfig.writeEntry("dialog_geometry", saveGeometry().toBase64()); titleConfig.writeEntry("font_family", font_family->currentFont()); //titleConfig.writeEntry("font_size", font_size->value()); titleConfig.writeEntry("font_pixel_size", font_size->value()); titleConfig.writeEntry("font_color", fontColorButton->color()); titleConfig.writeEntry("font_outline_color", textOutlineColor->color()); titleConfig.writeEntry("font_outline", textOutline->value()); titleConfig.writeEntry("font_weight", font_weight_box->itemData(font_weight_box->currentIndex()).toInt()); titleConfig.writeEntry("font_italic", buttonItalic->isChecked()); titleConfig.writeEntry("font_underlined", buttonUnder->isChecked()); titleConfig.writeEntry("rect_background_color", rectBColor->color()); titleConfig.writeEntry("rect_foreground_color", rectFColor->color()); titleConfig.writeEntry("rect_background_alpha", rectBColor->color().alpha()); titleConfig.writeEntry("rect_foreground_alpha", rectFColor->color().alpha()); titleConfig.writeEntry("rect_line_width", rectLineWidth->value()); titleConfig.writeEntry("background_color", backgroundColor->color()); titleConfig.writeEntry("background_alpha", backgroundAlpha->value()); titleConfig.writeEntry("use_grid", use_grid->isChecked()); //! \todo Not sure if I should sync - it is probably safe to do it config->sync(); } void TitleWidget::readChoices() { // Get a pointer to a shared configuration instance, then get the TitleWidget group. KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup titleConfig(config, "TitleWidget"); // read the entries const QByteArray geometry = titleConfig.readEntry("dialog_geometry", QByteArray()); restoreGeometry(QByteArray::fromBase64(geometry)); font_family->setCurrentFont(titleConfig.readEntry("font_family", font_family->currentFont())); font_size->setValue(titleConfig.readEntry("font_pixel_size", font_size->value())); m_scene->slotUpdateFontSize(font_size->value()); QColor fontColor = QColor(titleConfig.readEntry("font_color", fontColorButton->color())); QColor outlineColor = QColor(titleConfig.readEntry("font_outline_color", textOutlineColor->color())); fontColor.setAlpha(titleConfig.readEntry("font_alpha", fontColor.alpha())); outlineColor.setAlpha(titleConfig.readEntry("font_outline_alpha", outlineColor.alpha())); fontColorButton->setColor(fontColor); textOutlineColor->setColor(outlineColor); textOutline->setValue(titleConfig.readEntry("font_outline", textOutline->value())); int weight; if (titleConfig.readEntry("font_bold", false)) { weight = QFont::Bold; } else { weight = titleConfig.readEntry("font_weight", font_weight_box->itemData(font_weight_box->currentIndex()).toInt()); } setFontBoxWeight(weight); buttonItalic->setChecked(titleConfig.readEntry("font_italic", buttonItalic->isChecked())); buttonUnder->setChecked(titleConfig.readEntry("font_underlined", buttonUnder->isChecked())); QColor fgColor = QColor(titleConfig.readEntry("rect_foreground_color", rectFColor->color())); QColor bgColor = QColor(titleConfig.readEntry("rect_background_color", rectBColor->color())); fgColor.setAlpha(titleConfig.readEntry("rect_foreground_alpha", fgColor.alpha())); bgColor.setAlpha(titleConfig.readEntry("rect_background_alpha", bgColor.alpha())); rectFColor->setColor(fgColor); rectBColor->setColor(bgColor); rectLineWidth->setValue(titleConfig.readEntry("rect_line_width", rectLineWidth->value())); backgroundColor->setColor(titleConfig.readEntry("background_color", backgroundColor->color())); backgroundAlpha->setValue(titleConfig.readEntry("background_alpha", backgroundAlpha->value())); use_grid->setChecked(titleConfig.readEntry("use_grid", false)); m_scene->slotUseGrid(use_grid->isChecked()); } void TitleWidget::adjustFrameSize() { m_frameWidth = m_titledocument.frameWidth(); m_frameHeight = m_titledocument.frameHeight(); m_frameBorder->setRect(0, 0, m_frameWidth, m_frameHeight); displayBackgroundFrame(); } void TitleWidget::slotAnimStart(bool anim) { if (anim && anim_end->isChecked()) { anim_end->setChecked(false); m_endViewport->setZValue(-1000); m_endViewport->setBrush(QBrush()); } slotSelectTool(); QList list = m_scene->items(); for (int i = 0; i < list.count(); ++i) { if (list.at(i)->zValue() > -1000) { if (!list.at(i)->data(-1).isNull()) { continue; } list.at(i)->setFlag(QGraphicsItem::ItemIsMovable, !anim); list.at(i)->setFlag(QGraphicsItem::ItemIsSelectable, !anim); } } align_box->setEnabled(anim); itemzoom->setEnabled(!anim); itemrotatex->setEnabled(!anim); itemrotatey->setEnabled(!anim); itemrotatez->setEnabled(!anim); frame_toolbar->setEnabled(!anim); toolbar_stack->setEnabled(!anim); if (anim) { keep_aspect->setChecked(!m_startViewport->data(0).isNull()); m_startViewport->setZValue(1100); QColor col = m_startViewport->pen().color(); col.setAlpha(100); m_startViewport->setBrush(col); m_startViewport->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); m_startViewport->setSelected(true); selectionChanged(); slotSelectTool(); if (m_startViewport->childItems().isEmpty()) { addAnimInfoText(); } } else { m_startViewport->setZValue(-1000); m_startViewport->setBrush(QBrush()); m_startViewport->setFlags(nullptr); if (!anim_end->isChecked()) { deleteAnimInfoText(); } } } void TitleWidget::slotAnimEnd(bool anim) { if (anim && anim_start->isChecked()) { anim_start->setChecked(false); m_startViewport->setZValue(-1000); m_startViewport->setBrush(QBrush()); } slotSelectTool(); QList list = m_scene->items(); for (int i = 0; i < list.count(); ++i) { if (list.at(i)->zValue() > -1000) { if (!list.at(i)->data(-1).isNull()) { continue; } list.at(i)->setFlag(QGraphicsItem::ItemIsMovable, !anim); list.at(i)->setFlag(QGraphicsItem::ItemIsSelectable, !anim); } } align_box->setEnabled(anim); itemzoom->setEnabled(!anim); itemrotatex->setEnabled(!anim); itemrotatey->setEnabled(!anim); itemrotatez->setEnabled(!anim); frame_toolbar->setEnabled(!anim); toolbar_stack->setEnabled(!anim); if (anim) { keep_aspect->setChecked(!m_endViewport->data(0).isNull()); m_endViewport->setZValue(1100); QColor col = m_endViewport->pen().color(); col.setAlpha(100); m_endViewport->setBrush(col); m_endViewport->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); m_endViewport->setSelected(true); selectionChanged(); slotSelectTool(); if (m_endViewport->childItems().isEmpty()) { addAnimInfoText(); } } else { m_endViewport->setZValue(-1000); m_endViewport->setBrush(QBrush()); m_endViewport->setFlags(nullptr); if (!anim_start->isChecked()) { deleteAnimInfoText(); } } } void TitleWidget::addAnimInfoText() { // add text to anim viewport QGraphicsTextItem *t = new QGraphicsTextItem(i18nc("Indicates the start of an animation", "Start"), m_startViewport); QGraphicsTextItem *t2 = new QGraphicsTextItem(i18nc("Indicates the end of an animation", "End"), m_endViewport); QFont font = t->font(); font.setPixelSize(m_startViewport->rect().width() / 10); QColor col = m_startViewport->pen().color(); col.setAlpha(255); t->setDefaultTextColor(col); t->setFont(font); font.setPixelSize(m_endViewport->rect().width() / 10); col = m_endViewport->pen().color(); col.setAlpha(255); t2->setDefaultTextColor(col); t2->setFont(font); } void TitleWidget::updateInfoText() { // update info text font if (!m_startViewport->childItems().isEmpty()) { MyTextItem *item = static_cast (m_startViewport->childItems().at(0)); if (item) { QFont font = item->font(); font.setPixelSize(m_startViewport->rect().width() / 10); item->setFont(font); } } if (!m_endViewport->childItems().isEmpty()) { MyTextItem *item = static_cast (m_endViewport->childItems().at(0)); if (item) { QFont font = item->font(); font.setPixelSize(m_endViewport->rect().width() / 10); item->setFont(font); } } } void TitleWidget::deleteAnimInfoText() { // end animation editing, remove info text while (!m_startViewport->childItems().isEmpty()) { QGraphicsItem *item = m_startViewport->childItems().at(0); if (m_scene) { m_scene->removeItem(item); } } while (!m_endViewport->childItems().isEmpty()) { QGraphicsItem *item = m_endViewport->childItems().at(0); if (m_scene) { m_scene->removeItem(item); } } } void TitleWidget::slotKeepAspect(bool keep) { if (m_endViewport->zValue() == 1100) { m_endViewport->setData(0, keep == true ? m_frameWidth : QVariant()); m_endViewport->setData(1, keep == true ? m_frameHeight : QVariant()); } else { m_startViewport->setData(0, keep == true ? m_frameWidth : QVariant()); m_startViewport->setData(1, keep == true ? m_frameHeight : QVariant()); } } void TitleWidget::slotResize50() { if (m_endViewport->zValue() == 1100) { m_endViewport->setRect(0, 0, m_frameWidth / 2, m_frameHeight / 2); } else { m_startViewport->setRect(0, 0, m_frameWidth / 2, m_frameHeight / 2); } } void TitleWidget::slotResize100() { if (m_endViewport->zValue() == 1100) { m_endViewport->setRect(0, 0, m_frameWidth, m_frameHeight); } else { m_startViewport->setRect(0, 0, m_frameWidth, m_frameHeight); } } void TitleWidget::slotResize200() { if (m_endViewport->zValue() == 1100) { m_endViewport->setRect(0, 0, m_frameWidth * 2, m_frameHeight * 2); } else { m_startViewport->setRect(0, 0, m_frameWidth * 2, m_frameHeight * 2); } } void TitleWidget::slotAddEffect(int /*ix*/) { QList list = graphicsView->scene()->selectedItems(); /* int effect = effect_list->itemData(ix).toInt(); if (list.size() == 1) { if (effect == NOEFFECT) effect_stack->setHidden(true); else { effect_stack->setCurrentIndex(effect - 1); effect_stack->setHidden(false); } } else // Hide the effects stack when more than one element is selected. effect_stack->setHidden(true); foreach(QGraphicsItem * item, list) { switch (effect) { case NOEFFECT: item->setData(100, QVariant()); item->setGraphicsEffect(0); break; case TYPEWRITEREFFECT: if (item->type() == TEXTITEM) { QStringList effdata = QStringList() << QStringLiteral("typewriter") << QString::number(typewriter_delay->value()) + QLatin1Char(';') + QString::number(typewriter_start->value()); item->setData(100, effdata); } break; // Do not remove the non-QGraphicsEffects. case BLUREFFECT: item->setGraphicsEffect(new QGraphicsBlurEffect()); break; case SHADOWEFFECT: item->setGraphicsEffect(new QGraphicsDropShadowEffect()); break; } }*/ } void TitleWidget::slotEditTypewriter(int /*ix*/) { QList l = graphicsView->scene()->selectedItems(); if (l.size() == 1) { QStringList effdata = QStringList() << QStringLiteral("typewriter") << QString::number(typewriter_delay->value()) + QLatin1Char(';') + QString::number(typewriter_start->value()); l[0]->setData(100, effdata); } } qreal TitleWidget::zIndexBounds(bool maxBound, bool intersectingOnly) { qreal bound = maxBound ? -99 : 99; const QList l = graphicsView->scene()->selectedItems(); if (!l.isEmpty()) { QList lItems; // Get items (all or intersecting only) if (intersectingOnly) { lItems = graphicsView->scene()->items(l[0]->sceneBoundingRect(), Qt::IntersectsItemShape); } else { lItems = graphicsView->scene()->items(); } if (!lItems.isEmpty()) { int n = lItems.size(); qreal z; if (maxBound) { for (int i = 0; i < n; ++i) { z = lItems[i]->zValue(); if (z > bound && !lItems[i]->isSelected()) { bound = z; } else if (z - 1 > bound) { // To get the maximum index even if it is of an item of the current selection. // Used when updating multiple items, to get all to the same level. // Otherwise, the maximum would stay at -99 if the highest item is in the selection. bound = z - 1; } } } else { // Get minimum z index. for (int i = 0; i < n; ++i) { z = lItems[i]->zValue(); if (z < bound && !lItems[i]->isSelected() && z > -999) { // There are items at the very bottom (background e.g.) with z-index < -1000. bound = z; } else if (z + 1 < bound && z > -999) { bound = z + 1; } } } } } return bound; } void TitleWidget::slotZIndexUp() { QList l = graphicsView->scene()->selectedItems(); if (l.size() >= 1) { qreal currentZ = l[0]->zValue(); qreal max = zIndexBounds(true, true); if (currentZ <= max) { l[0]->setZValue(currentZ + 1); updateDimension(l[0]); } } } void TitleWidget::slotZIndexTop() { QList l = graphicsView->scene()->selectedItems(); qreal max = zIndexBounds(true, false); //qCDebug(KDENLIVE_LOG) << "Max z-index is " << max << ".\n"; for (int i = 0; i < l.size(); ++i) { qreal currentZ = l[i]->zValue(); if (currentZ <= max) { //qCDebug(KDENLIVE_LOG) << "Updating item " << i << ", is " << currentZ << ".\n"; l[i]->setZValue(max + 1); } else { //qCDebug(KDENLIVE_LOG) << "Not updating " << i << ", is " << currentZ << ".\n"; } } // Update the z index value in the GUI if (!l.isEmpty()) { updateDimension(l[0]); } } void TitleWidget::slotZIndexDown() { QList l = graphicsView->scene()->selectedItems(); if (l.size() >= 1) { qreal currentZ = l[0]->zValue(); qreal min = zIndexBounds(false, true); if (currentZ >= min) { l[0]->setZValue(currentZ - 1); updateDimension(l[0]); } } } void TitleWidget::slotZIndexBottom() { QList l = graphicsView->scene()->selectedItems(); qreal min = zIndexBounds(false, false); for (int i = 0; i < l.size(); ++i) { qreal currentZ = l[i]->zValue(); if (currentZ >= min) { l[i]->setZValue(min - 1); } } // Update the z index value in the GUI if (!l.isEmpty()) { updateDimension(l[0]); } } void TitleWidget::slotSelectAll() { QList l = graphicsView->scene()->items(); for (int i = 0; i < l.size(); ++i) { l.at(i)->setSelected(true); } } void TitleWidget::selectItems(int itemType) { QList l; if (!graphicsView->scene()->selectedItems().isEmpty()) { l = graphicsView->scene()->selectedItems(); for (int i = 0; i < l.size(); ++i) { if (l.at(i)->type() != itemType) { l.at(i)->setSelected(false); } } } else { l = graphicsView->scene()->items(); for (int i = 0; i < l.size(); ++i) { if (l.at(i)->type() == itemType) { l.at(i)->setSelected(true); } } } } void TitleWidget::slotSelectText() { selectItems(TEXTITEM); } void TitleWidget::slotSelectRects() { selectItems(RECTITEM); } void TitleWidget::slotSelectImages() { selectItems(IMAGEITEM); } void TitleWidget::slotSelectNone() { graphicsView->blockSignals(true); QList l = graphicsView->scene()->items(); for (int i = 0; i < l.size(); ++i) { l.at(i)->setSelected(false); } graphicsView->blockSignals(false); selectionChanged(); } QString TitleWidget::getTooltipWithShortcut(const QString &text, QAction *button) { return text + QStringLiteral(" ") + button->shortcut().toString() + QStringLiteral(""); } void TitleWidget::prepareTools(QGraphicsItem *referenceItem) { // Let some GUI elements block signals. We may want to change their values without any sideeffects. // Additionally, store the previous blocking state to avoid side effects when this function is called from within another one. // Note: Disabling an element also blocks signals. So disabled elements don't need to be set to blocking too. bool blockOX = origin_x_left->signalsBlocked(); bool blockOY = origin_y_top->signalsBlocked(); bool blockEff = effect_list->signalsBlocked(); bool blockRX = itemrotatex->signalsBlocked(); bool blockRY = itemrotatey->signalsBlocked(); bool blockRZ = itemrotatez->signalsBlocked(); bool blockZoom = itemzoom->signalsBlocked(); bool blockX = value_x->signalsBlocked(); bool blockY = value_y->signalsBlocked(); bool blockW = value_w->signalsBlocked(); bool blockH = value_h->signalsBlocked(); origin_x_left->blockSignals(true); origin_y_top->blockSignals(true); effect_list->blockSignals(true); itemrotatex->blockSignals(true); itemrotatey->blockSignals(true); itemrotatez->blockSignals(true); itemzoom->blockSignals(true); value_x->blockSignals(true); value_y->blockSignals(true); value_w->blockSignals(true); value_h->blockSignals(true); if (referenceItem == nullptr) { //qCDebug(KDENLIVE_LOG) << "nullptr item.\n"; effect_list->setCurrentIndex(0); origin_x_left->setChecked(false); origin_y_top->setChecked(false); updateTextOriginX(); updateTextOriginY(); enableToolbars(TITLE_SELECT); showToolbars(TITLE_SELECT); itemzoom->setEnabled(false); itemrotatex->setEnabled(false); itemrotatey->setEnabled(false); itemrotatez->setEnabled(false); frame_properties->setEnabled(false); toolbar_stack->setEnabled(false); /*letter_spacing->setEnabled(false); line_spacing->setEnabled(false); letter_spacing->setValue(0); line_spacing->setValue(0);*/ } else { toolbar_stack->setEnabled(true); frame_properties->setEnabled(true); if (referenceItem != m_startViewport && referenceItem != m_endViewport) { itemzoom->setEnabled(true); itemrotatex->setEnabled(true); itemrotatey->setEnabled(true); itemrotatez->setEnabled(true); } else { itemzoom->setEnabled(false); itemrotatex->setEnabled(false); itemrotatey->setEnabled(false); itemrotatez->setEnabled(false); updateInfoText(); } letter_spacing->setEnabled(referenceItem->type() == TEXTITEM); line_spacing->setEnabled(referenceItem->type() == TEXTITEM); if (referenceItem->type() == TEXTITEM) { showToolbars(TITLE_TEXT); MyTextItem *i = static_cast (referenceItem); if (!i->document()->isEmpty()) { // We have an existing text item selected if (!i->data(100).isNull()) { // Item has an effect QStringList effdata = i->data(100).toStringList(); QString effectName = effdata.takeFirst(); if (effectName == QLatin1String("typewriter")) { QStringList params = effdata.at(0).split(QLatin1Char(';')); typewriter_delay->setValue(params.at(0).toInt()); typewriter_start->setValue(params.at(1).toInt()); effect_list->setCurrentIndex(effect_list->findData((int) TYPEWRITEREFFECT)); } } else { /*if (i->graphicsEffect()) { QGraphicsBlurEffect *blur = static_cast (i->graphicsEffect()); if (blur) { effect_list->setCurrentIndex(effect_list->findData((int) BLUREFFECT)); int rad = (int) blur->blurRadius(); blur_radius->setValue(rad); effect_stack->setHidden(false); } else { QGraphicsDropShadowEffect *shad = static_cast (i->graphicsEffect()); if (shad) { effect_list->setCurrentIndex(effect_list->findData((int) SHADOWEFFECT)); shadow_radius->setValue(shad->blurRadius()); shadow_x->setValue(shad->xOffset()); shadow_y->setValue(shad->yOffset()); effect_stack->setHidden(false); } } } else { effect_list->setCurrentIndex(effect_list->findData((int) NOEFFECT)); effect_stack->setHidden(true); }*/ } font_size->blockSignals(true); font_family->blockSignals(true); font_weight_box->blockSignals(true); buttonItalic->blockSignals(true); buttonUnder->blockSignals(true); fontColorButton->blockSignals(true); buttonAlignLeft->blockSignals(true); buttonAlignRight->blockSignals(true); buttonAlignCenter->blockSignals(true); QFont font = i->font(); font_family->setCurrentFont(font); font_size->setValue(font.pixelSize()); m_scene->slotUpdateFontSize(font.pixelSize()); buttonItalic->setChecked(font.italic()); buttonUnder->setChecked(font.underline()); setFontBoxWeight(font.weight()); QTextCursor cursor(i->document()); cursor.select(QTextCursor::Document); QColor color = cursor.charFormat().foreground().color(); fontColorButton->setColor(color); if (!i->data(TitleDocument::OutlineWidth).isNull()) { textOutline->blockSignals(true); textOutline->setValue(i->data(TitleDocument::OutlineWidth).toDouble() * 10); textOutline->blockSignals(false); } else { textOutline->blockSignals(true); textOutline->setValue(0); textOutline->blockSignals(false); } if (!i->data(TitleDocument::OutlineColor).isNull()) { textOutlineColor->blockSignals(true); QVariant variant = i->data(TitleDocument::OutlineColor); color = variant.value(); textOutlineColor->setColor(color); textOutlineColor->blockSignals(false); } if (!i->data(TitleDocument::Gradient).isNull()) { gradients_combo->blockSignals(true); gradient_color->setChecked(true); QString gradientData = i->data(TitleDocument::Gradient).toString(); int ix = gradients_combo->findData(gradientData); if (ix == -1) { // This gradient does not exist in our settings, store it storeGradient(gradientData); ix = gradients_combo->findData(gradientData); } gradients_combo->setCurrentIndex(ix); gradients_combo->blockSignals(false); } else { plain_color->setChecked(true); } if (i->alignment() == Qt::AlignHCenter) { buttonAlignCenter->setChecked(true); } else if (i->alignment() == Qt::AlignRight) { buttonAlignRight->setChecked(true); } else { buttonAlignLeft->setChecked(true); } QStringList sInfo = i->shadowInfo(); if (sInfo.count() >= 5) { shadowBox->setChecked(sInfo.at(0).toInt() == true); shadowBox->blockSignals(true); shadowColor->setColor(QColor(sInfo.at(1))); blur_radius->setValue(sInfo.at(2).toInt()); shadowX->setValue(sInfo.at(3).toInt()); shadowY->setValue(sInfo.at(4).toInt()); shadowBox->blockSignals(false); } letter_spacing->blockSignals(true); line_spacing->blockSignals(true); QTextCursor cur = i->textCursor(); QTextBlockFormat format = cur.blockFormat(); letter_spacing->setValue(font.letterSpacing()); line_spacing->setValue(format.lineHeight()); letter_spacing->blockSignals(false); line_spacing->blockSignals(false); font_size->blockSignals(false); font_family->blockSignals(false); font_weight_box->blockSignals(false); buttonItalic->blockSignals(false); buttonUnder->blockSignals(false); fontColorButton->blockSignals(false); buttonAlignLeft->blockSignals(false); buttonAlignRight->blockSignals(false); buttonAlignCenter->blockSignals(false); // mbt 1607: Select text if the text item is an unchanged template item. if (i->property("isTemplate").isValid()) { cur.setPosition(0, QTextCursor::MoveAnchor); cur.select(QTextCursor::Document); i->setTextCursor(cur); // Make text editable now. i->grabKeyboard(); i->setTextInteractionFlags(Qt::TextEditorInteraction); } } updateAxisButtons(i); updateCoordinates(i); updateDimension(i); enableToolbars(TITLE_TEXT); } else if ((referenceItem)->type() == RECTITEM) { showToolbars(TITLE_RECTANGLE); settingUp = true; QGraphicsRectItem *rec = static_cast (referenceItem); if (rec == m_startViewport || rec == m_endViewport) { enableToolbars(TITLE_SELECT); } else { QColor fcol = rec->pen().color(); QColor bcol = rec->brush().color(); rectFColor->setColor(fcol); QString gradientData = rec->data(TitleDocument::Gradient).toString(); if (gradientData.isEmpty()) { plain_rect->setChecked(true); rectBColor->setColor(bcol); } else { gradient_rect->setChecked(true); gradients_rect_combo->blockSignals(true); int ix = gradients_rect_combo->findData(gradientData); if (ix == -1) { storeGradient(gradientData); ix = gradients_rect_combo->findData(gradientData); } gradients_rect_combo->setCurrentIndex(ix); gradients_rect_combo->blockSignals(false); } settingUp = false; if (rec->pen() == Qt::NoPen) { rectLineWidth->setValue(0); } else { rectLineWidth->setValue(rec->pen().width()); } enableToolbars(TITLE_RECTANGLE); } updateAxisButtons(referenceItem); updateCoordinates(rec); updateDimension(rec); } else if (referenceItem->type() == IMAGEITEM) { showToolbars(TITLE_IMAGE); updateCoordinates(referenceItem); updateDimension(referenceItem); enableToolbars(TITLE_IMAGE); } else { showToolbars(TITLE_SELECT); enableToolbars(TITLE_SELECT); frame_properties->setEnabled(false); } zValue->setValue((int)referenceItem->zValue()); if (!referenceItem->data(TitleDocument::ZoomFactor).isNull()) { itemzoom->setValue(referenceItem->data(TitleDocument::ZoomFactor).toInt()); } else { itemzoom->setValue((int)(m_transformations.value(referenceItem).scalex * 100.0 + 0.5)); } itemrotatex->setValue((int)(m_transformations.value(referenceItem).rotatex)); itemrotatey->setValue((int)(m_transformations.value(referenceItem).rotatey)); itemrotatez->setValue((int)(m_transformations.value(referenceItem).rotatez)); } effect_list->blockSignals(blockEff); itemrotatex->blockSignals(blockRX); itemrotatey->blockSignals(blockRY); itemrotatez->blockSignals(blockRZ); itemzoom->blockSignals(blockZoom); origin_x_left->blockSignals(blockOX); origin_y_top->blockSignals(blockOY); value_x->blockSignals(blockX); value_y->blockSignals(blockY); value_w->blockSignals(blockW); value_h->blockSignals(blockH); } void TitleWidget::slotEditGradient() { QToolButton *caller = qobject_cast(QObject::sender()); if (!caller) { return; } QComboBox *combo = nullptr; if (caller == edit_gradient) { combo = gradients_combo; } else { combo = gradients_rect_combo; } QMap gradients; for (int i = 0; i < combo->count(); i++) { gradients.insert(combo->itemText(i), combo->itemData(i).toString()); } GradientWidget d(gradients, combo->currentIndex()); if (d.exec() == QDialog::Accepted) { // Save current gradients QMap gradients = d.gradients(); QList icons = d.icons(); QMap::const_iterator i = gradients.constBegin(); KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup group(config, "TitleGradients"); group.deleteGroup(); combo->clear(); gradients_rect_combo->clear(); int ix = 0; while (i != gradients.constEnd()) { group.writeEntry(i.key(), i.value()); gradients_combo->addItem(icons.at(ix), i.key(), i.value()); gradients_rect_combo->addItem(icons.at(ix), i.key(), i.value()); ++i; ix++; } group.sync(); combo->setCurrentIndex(d.selectedGradient()); } } void TitleWidget::storeGradient(const QString &gradientData) { KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup group(config, "TitleGradients"); QMap values = group.entryMap(); int ix = qMax(1, values.count()); QString gradName = i18n("Gradient %1", ix); while (values.contains(gradName)) { ix++; gradName = i18n("Gradient %1", ix); } group.writeEntry(gradName, gradientData); group.sync(); QPixmap pix(30, 30); pix.fill(Qt::transparent); QLinearGradient gr = GradientWidget::gradientFromString(gradientData, pix.width(), pix.height()); gr.setStart(0, pix.height() / 2); gr.setFinalStop(pix.width(), pix.height() / 2); QPainter painter(&pix); painter.fillRect(0, 0, pix.width(), pix.height(), QBrush(gr)); painter.end(); QIcon icon(pix); gradients_combo->addItem(icon, gradName, gradientData); gradients_rect_combo->addItem(icon, gradName, gradientData); } void TitleWidget::loadGradients() { QMap gradients; gradients_combo->blockSignals(true); gradients_rect_combo->blockSignals(true); QString data = gradients_combo->currentData().toString(); QString rect_data = gradients_rect_combo->currentData().toString(); gradients_combo->clear(); gradients_rect_combo->clear(); KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup group(config, "TitleGradients"); QMap values = group.entryMap(); if (values.isEmpty()) { // Ensure we at least always have one sample black to white gradient values.insert(i18n("Gradient"), QStringLiteral("#ffffffff;#ff000000;0;100;90")); } QMapIterator k(values); while (k.hasNext()) { k.next(); QPixmap pix(30, 30); pix.fill(Qt::transparent); QLinearGradient gr = GradientWidget::gradientFromString(k.value(), pix.width(), pix.height()); gr.setStart(0, pix.height() / 2); gr.setFinalStop(pix.width(), pix.height() / 2); QPainter painter(&pix); painter.fillRect(0, 0, pix.width(), pix.height(), QBrush(gr)); painter.end(); QIcon icon(pix); gradients_combo->addItem(icon, k.key(), k.value()); gradients_rect_combo->addItem(icon, k.key(), k.value()); } int ix = gradients_combo->findData(data); if (ix >= 0) { gradients_combo->setCurrentIndex(ix); } ix = gradients_rect_combo->findData(rect_data); if (ix >= 0) { gradients_rect_combo->setCurrentIndex(ix); } gradients_combo->blockSignals(false); gradients_rect_combo->blockSignals(false); } void TitleWidget::slotUpdateShadow() { QList l = graphicsView->scene()->selectedItems(); for (int i = 0; i < graphicsView->scene()->selectedItems().length(); ++i) { MyTextItem *item = nullptr; if (l.at(i)->type() == TEXTITEM) { item = static_cast (l.at(i)); } if (!item) { // No text item, try next one. continue; } item->updateShadow(shadowBox->isChecked(), blur_radius->value(), shadowX->value(), shadowY->value(), shadowColor->color()); } }