File indexing completed on 2024-04-21 04:51:21
0001 /* 0002 SPDX-FileCopyrightText: 2016 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "profilesdialog.h" 0008 #include "core.h" 0009 #include "effects/effectsrepository.hpp" 0010 #include "kdenlivesettings.h" 0011 #include "profiles/profilemodel.hpp" 0012 #include "profiles/profilerepository.hpp" 0013 0014 #include "klocalizedstring.h" 0015 #include "utils/KMessageBox_KdenliveCompat.h" 0016 #include <KMessageBox> 0017 #include <KMessageWidget> 0018 0019 #include "kdenlive_debug.h" 0020 #include <QCloseEvent> 0021 #include <QDir> 0022 #include <QStandardPaths> 0023 0024 ProfilesDialog::ProfilesDialog(const QString &profileDescription, QWidget *parent) 0025 : QDialog(parent) 0026 { 0027 // ask profile repository for a refresh 0028 ProfileRepository::get()->refresh(); 0029 0030 m_view.setupUi(this); 0031 showMessage(); 0032 0033 // Fill colorspace list (see mlt_profile.h) 0034 m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(601), 601); 0035 m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(709), 709); 0036 m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(240), 240); 0037 m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(0), 0); 0038 0039 QStringList profilesFilter; 0040 profilesFilter << QStringLiteral("*"); 0041 0042 fillList(profileDescription); 0043 slotUpdateDisplay(); 0044 connectDialog(); 0045 } 0046 0047 void ProfilesDialog::connectDialog() 0048 { 0049 connect(m_view.profiles_list, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0050 [&](int ix) { slotUpdateDisplay(m_view.profiles_list->itemData(ix).toString()); }); 0051 connect(m_view.button_create, &QAbstractButton::clicked, this, &ProfilesDialog::slotCreateProfile); 0052 connect(m_view.button_save, &QAbstractButton::clicked, this, &ProfilesDialog::slotSaveProfile); 0053 connect(m_view.button_delete, &QAbstractButton::clicked, this, &ProfilesDialog::slotDeleteProfile); 0054 connect(m_view.button_default, &QAbstractButton::clicked, this, &ProfilesDialog::slotSetDefaultProfile); 0055 0056 connect(m_view.description, &QLineEdit::textChanged, this, &ProfilesDialog::slotProfileEdited); 0057 connect(m_view.frame_num, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited); 0058 connect(m_view.frame_den, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited); 0059 connect(m_view.aspect_num, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited); 0060 connect(m_view.aspect_den, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited); 0061 connect(m_view.display_num, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited); 0062 connect(m_view.display_den, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited); 0063 connect(m_view.scanning, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ProfilesDialog::slotProfileEdited); 0064 connect(m_view.scanning, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ProfilesDialog::slotScanningChanged); 0065 connect(m_view.size_h, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited); 0066 connect(m_view.size_h, &QAbstractSpinBox::editingFinished, this, &ProfilesDialog::slotAdjustHeight); 0067 m_view.size_h->setSingleStep(2); 0068 connect(m_view.size_w, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ProfilesDialog::slotProfileEdited); 0069 connect(m_view.size_w, &QAbstractSpinBox::editingFinished, this, &ProfilesDialog::slotAdjustWidth); 0070 m_view.size_w->setSingleStep(2); 0071 } 0072 0073 ProfilesDialog::ProfilesDialog(const QString &profilePath, bool, QWidget *parent) 0074 : QDialog(parent) 0075 , m_isCustomProfile(true) 0076 , m_customProfilePath(profilePath) 0077 { 0078 m_view.setupUi(this); 0079 showMessage(); 0080 0081 // Fill colorspace list (see mlt_profile.h) 0082 m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(601), 601); 0083 m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(709), 709); 0084 m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(240), 240); 0085 m_view.colorspace->addItem(ProfileRepository::getColorspaceDescription(0), 0); 0086 0087 QStringList profilesFilter; 0088 profilesFilter << QStringLiteral("*"); 0089 0090 m_view.button_create->setHidden(true); 0091 m_view.profiles_list->setHidden(true); 0092 m_view.button_delete->setHidden(true); 0093 m_view.button_default->setHidden(true); 0094 m_view.description->setEnabled(false); 0095 0096 slotUpdateDisplay(profilePath); 0097 connectDialog(); 0098 } 0099 0100 void ProfilesDialog::slotAdjustWidth() 0101 { 0102 // A profile's width should always be a multiple of 2 0103 QSignalBlocker blk(m_view.size_w); 0104 int val = m_view.size_w->value(); 0105 int correctedWidth = val + (val % 2); 0106 if (val == correctedWidth) { 0107 // Ok, no action required, width is a multiple of 2 0108 showMessage(); 0109 } else { 0110 m_view.size_w->setValue(correctedWidth); 0111 showMessage(i18n("Profile width must be a multiple of 2. It was adjusted to %1", correctedWidth)); 0112 } 0113 } 0114 0115 void ProfilesDialog::slotAdjustHeight() 0116 { 0117 // A profile's height should always be a multiple of 2 0118 QSignalBlocker blk(m_view.size_h); 0119 int val = m_view.size_h->value(); 0120 int correctedHeight = val + (val % 2); 0121 if (val == correctedHeight) { 0122 // Ok, no action required, height is a multiple of 2 0123 showMessage(); 0124 } else { 0125 m_view.size_h->setValue(correctedHeight); 0126 showMessage(i18n("Profile height must be a multiple of 2. It was adjusted to %1", correctedHeight)); 0127 } 0128 } 0129 0130 void ProfilesDialog::slotScanningChanged(int ix) 0131 { 0132 m_view.field_order->setEnabled(ix == 0); 0133 m_view.label_field_order->setEnabled(ix == 0); 0134 if (ix == 0 && !EffectsRepository::get()->hasInternalEffect(QStringLiteral("avfilter.fieldorder"))) { 0135 m_view.effect_warning->show(); 0136 } else { 0137 m_view.effect_warning->hide(); 0138 } 0139 } 0140 0141 void ProfilesDialog::slotProfileEdited() 0142 { 0143 m_profileIsModified = true; 0144 } 0145 0146 void ProfilesDialog::fillList(const QString &selectedProfile) 0147 { 0148 m_view.profiles_list->clear(); 0149 // Retrieve the list from the repository 0150 QVector<QPair<QString, QString>> profiles = ProfileRepository::get()->getAllProfiles(); 0151 for (const auto &p : qAsConst(profiles)) { 0152 m_view.profiles_list->addItem(p.first, p.second); 0153 } 0154 0155 if (!KdenliveSettings::default_profile().isEmpty()) { 0156 int ix = m_view.profiles_list->findData(KdenliveSettings::default_profile()); 0157 if (ix > -1) { 0158 m_view.profiles_list->setCurrentIndex(ix); 0159 } else { 0160 // Error, profile not found 0161 qCWarning(KDENLIVE_LOG) << "Project profile not found, disable editing"; 0162 } 0163 } 0164 int ix = m_view.profiles_list->findText(selectedProfile); 0165 if (ix != -1) { 0166 m_view.profiles_list->setCurrentIndex(ix); 0167 } 0168 m_selectedProfileIndex = m_view.profiles_list->currentIndex(); 0169 } 0170 0171 void ProfilesDialog::accept() 0172 { 0173 if (askForSave()) { 0174 QDialog::accept(); 0175 } 0176 } 0177 0178 void ProfilesDialog::reject() 0179 { 0180 if (askForSave()) { 0181 QDialog::reject(); 0182 } 0183 } 0184 0185 void ProfilesDialog::closeEvent(QCloseEvent *event) 0186 { 0187 if (askForSave()) { 0188 event->accept(); 0189 } else { 0190 event->ignore(); 0191 } 0192 } 0193 0194 bool ProfilesDialog::askForSave() 0195 { 0196 if (!m_profileIsModified) { 0197 return true; 0198 } 0199 if (KMessageBox::questionTwoActions(this, i18n("The custom profile was modified, do you want to save it?"), {}, KStandardGuiItem::save(), 0200 KStandardGuiItem::discard()) != KMessageBox::PrimaryAction) { 0201 return true; 0202 } 0203 return slotSaveProfile(); 0204 } 0205 0206 void ProfilesDialog::slotCreateProfile() 0207 { 0208 m_view.button_delete->setEnabled(false); 0209 m_view.button_create->setEnabled(false); 0210 m_view.button_save->setEnabled(true); 0211 m_view.properties->setEnabled(true); 0212 m_view.description->blockSignals(true); 0213 m_view.description->setText(m_view.description->text() + " " + i18n("(copy)")); 0214 m_view.description->blockSignals(false); 0215 } 0216 0217 void ProfilesDialog::slotSetDefaultProfile() 0218 { 0219 if (m_profileIsModified) { 0220 showMessage(i18n("Save your profile before setting it to default")); 0221 return; 0222 } 0223 int ix = m_view.profiles_list->currentIndex(); 0224 QString path = m_view.profiles_list->itemData(ix).toString(); 0225 if (!path.isEmpty()) { 0226 KdenliveSettings::setDefault_profile(path); 0227 } 0228 } 0229 0230 bool ProfilesDialog::slotSaveProfile() 0231 { 0232 slotAdjustWidth(); 0233 0234 if (!m_customProfilePath.isEmpty()) { 0235 saveProfile(m_customProfilePath); 0236 return true; 0237 } 0238 const QString profileDesc = m_view.description->text(); 0239 int ix = m_view.profiles_list->findText(profileDesc); 0240 if (ix != -1) { 0241 // this profile name already exists 0242 const QString path = m_view.profiles_list->itemData(ix).toString(); 0243 if (!path.contains(QLatin1Char('/'))) { 0244 KMessageBox::error( 0245 this, i18n("A profile with same name already exists in MLT's default profiles, please choose another description for your custom profile.")); 0246 return false; 0247 } 0248 saveProfile(path); 0249 } else { 0250 int i = 0; 0251 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/")); 0252 if (!dir.exists()) { 0253 dir.mkpath(QStringLiteral(".")); 0254 } 0255 QString customName = QStringLiteral("customprofile"); 0256 QString profilePath = dir.absoluteFilePath(customName + QString::number(i)); 0257 while (QFile::exists(profilePath)) { 0258 ++i; 0259 profilePath = dir.absoluteFilePath(customName + QString::number(i)); 0260 } 0261 saveProfile(profilePath); 0262 } 0263 m_profileIsModified = false; 0264 fillList(profileDesc); 0265 m_view.button_create->setEnabled(true); 0266 m_profilesChanged = true; 0267 return true; 0268 } 0269 0270 void ProfilesDialog::saveProfile(const QString &path) 0271 { 0272 std::unique_ptr<ProfileParam> profile(new ProfileParam(pCore->getCurrentProfile().get())); 0273 profile->m_description = m_view.description->text(); 0274 profile->m_frame_rate_num = m_view.frame_num->value(); 0275 profile->m_frame_rate_den = m_view.frame_den->value(); 0276 profile->m_width = m_view.size_w->value(); 0277 profile->m_height = m_view.size_h->value(); 0278 profile->m_progressive = m_view.scanning->currentIndex() == 1; 0279 profile->m_bottom_field_first = m_view.field_order->currentIndex() == 1; 0280 profile->m_sample_aspect_num = m_view.aspect_num->value(); 0281 profile->m_sample_aspect_den = m_view.aspect_den->value(); 0282 profile->m_display_aspect_num = m_view.display_num->value(); 0283 profile->m_display_aspect_den = m_view.display_den->value(); 0284 profile->m_colorspace = m_view.colorspace->itemData(m_view.colorspace->currentIndex()).toInt(); 0285 ProfileRepository::get()->saveProfile(profile.get(), path); 0286 } 0287 0288 void ProfilesDialog::slotDeleteProfile() 0289 { 0290 const QString path = m_view.profiles_list->itemData(m_view.profiles_list->currentIndex()).toString(); 0291 bool success = ProfileRepository::get()->deleteProfile(path); 0292 if (success) { 0293 m_profilesChanged = true; 0294 fillList(); 0295 } 0296 } 0297 0298 void ProfilesDialog::slotUpdateDisplay(QString currentProfilePath) 0299 { 0300 qDebug() << "/ / / /UPDATING DISPLAY FOR PROFILE: " << currentProfilePath; 0301 if (!askForSave()) { 0302 m_view.profiles_list->blockSignals(true); 0303 m_view.profiles_list->setCurrentIndex(m_selectedProfileIndex); 0304 m_view.profiles_list->blockSignals(false); 0305 return; 0306 } 0307 QLocale locale; // Used for UI output → OK 0308 locale.setNumberOptions(QLocale::OmitGroupSeparator); 0309 m_selectedProfileIndex = m_view.profiles_list->currentIndex(); 0310 if (currentProfilePath.isEmpty()) { 0311 currentProfilePath = m_view.profiles_list->itemData(m_view.profiles_list->currentIndex()).toString(); 0312 } 0313 m_isCustomProfile = currentProfilePath.contains(QLatin1Char('/')); 0314 // Don't allow editing of the current Project, since this produces crashes at the moment 0315 bool isCurrentlyUsed = pCore->getCurrentProfilePath() == currentProfilePath; 0316 showMessage(isCurrentlyUsed ? i18n("The profile of the current project cannot be edited while the project is open.") : QString()); 0317 m_view.button_create->setEnabled(true); 0318 m_view.button_delete->setEnabled(m_isCustomProfile && !isCurrentlyUsed); 0319 m_view.properties->setEnabled(m_isCustomProfile && !isCurrentlyUsed); 0320 m_view.button_save->setEnabled(m_isCustomProfile && !isCurrentlyUsed); 0321 std::unique_ptr<ProfileModel> &curProfile = ProfileRepository::get()->getProfile(currentProfilePath); 0322 m_view.description->setText(curProfile->description()); 0323 m_view.size_w->setValue(curProfile->width()); 0324 m_view.size_h->setValue(curProfile->height()); 0325 m_view.aspect_num->setValue(curProfile->sample_aspect_num()); 0326 m_view.aspect_den->setValue(curProfile->sample_aspect_den()); 0327 m_view.display_num->setValue(curProfile->display_aspect_num()); 0328 m_view.display_den->setValue(curProfile->display_aspect_den()); 0329 m_view.frame_num->setValue(curProfile->frame_rate_num()); 0330 m_view.frame_den->setValue(curProfile->frame_rate_den()); 0331 m_view.scanning->setCurrentIndex(curProfile->progressive() ? 1 : 0); 0332 m_view.field_order->setCurrentIndex(curProfile->bottom_field_first() ? 1 : 0); 0333 slotScanningChanged(m_view.scanning->currentIndex()); 0334 if (curProfile->progressive() != 0) { 0335 m_view.fields->setText(locale.toString(double(curProfile->frame_rate_num() / curProfile->frame_rate_den()), 'f', 2)); 0336 } else { 0337 m_view.fields->setText(locale.toString(2.0 * curProfile->frame_rate_num() / curProfile->frame_rate_den(), 'f', 2)); 0338 } 0339 0340 int colorix = m_view.colorspace->findData(curProfile->colorspace()); 0341 if (colorix > -1) { 0342 m_view.colorspace->setCurrentIndex(colorix); 0343 } 0344 m_profileIsModified = false; 0345 } 0346 0347 bool ProfilesDialog::profileTreeChanged() const 0348 { 0349 return m_profilesChanged; 0350 } 0351 0352 void ProfilesDialog::showMessage(const QString &text, KMessageWidget::MessageType type) 0353 { 0354 if (text.isEmpty()) { 0355 m_view.info_message->hide(); 0356 } else { 0357 m_view.info_message->setText(text); 0358 m_view.info_message->setMessageType(type); 0359 m_view.info_message->animatedShow(); 0360 } 0361 }