File indexing completed on 2024-04-28 04:52:14
0001 /* 0002 SPDX-FileCopyrightText: 2016 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 SPDX-FileCopyrightText: 2017 Nicolas Carion 0004 This file is part of Kdenlive. See www.kdenlive.org. 0005 0006 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #include "profilewidget.h" 0010 #include "kdenlivesettings.h" 0011 #include "kxmlgui_version.h" 0012 #include "profiles/profilemodel.hpp" 0013 #include "profiles/profilerepository.hpp" 0014 #include "profiles/tree/profilefilter.hpp" 0015 #include "profiles/tree/profiletreemodel.hpp" 0016 0017 #include <KLocalizedString> 0018 #include <QComboBox> 0019 #include <QHeaderView> 0020 #include <QLabel> 0021 #include <QSplitter> 0022 #include <QTextEdit> 0023 #include <QTreeView> 0024 0025 ProfileWidget::ProfileWidget(QWidget *parent) 0026 : QWidget(parent) 0027 , m_originalProfile(QStringLiteral("invalid")) 0028 { 0029 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); 0030 auto *lay = new QVBoxLayout; 0031 lay->setContentsMargins(0, 0, 0, 0); 0032 auto *labelLay = new QHBoxLayout; 0033 auto *fpsLabel = new QLabel(i18n("Fps:"), this); 0034 m_fpsFilt = new QComboBox(this); 0035 fpsLabel->setBuddy(m_fpsFilt); 0036 labelLay->addWidget(fpsLabel); 0037 labelLay->addWidget(m_fpsFilt); 0038 0039 auto *scanningLabel = new QLabel(i18n("Scanning:"), this); 0040 m_scanningFilt = new QComboBox(this); 0041 scanningLabel->setBuddy(m_scanningFilt); 0042 labelLay->addWidget(scanningLabel); 0043 labelLay->addWidget(m_scanningFilt); 0044 labelLay->addStretch(1); 0045 0046 auto *manage_profiles = new QToolButton(this); 0047 labelLay->addWidget(manage_profiles); 0048 manage_profiles->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); 0049 manage_profiles->setToolTip(i18n("Manage project profiles")); 0050 manage_profiles->setWhatsThis( 0051 xi18nc("@info:whatsthis", 0052 "Opens the profile dialog window in which you can change project profiles. Note: The profile used in the open project cannot be changed.")); 0053 connect(manage_profiles, &QAbstractButton::clicked, this, &ProfileWidget::slotEditProfiles); 0054 lay->addLayout(labelLay); 0055 0056 auto *profileSplitter = new QSplitter; 0057 0058 m_treeView = new QTreeView(this); 0059 m_treeModel = ProfileTreeModel::construct(this); 0060 m_filter = new ProfileFilter(this); 0061 m_filter->setSourceModel(m_treeModel.get()); 0062 m_treeView->setModel(m_filter); 0063 for (int i = 1; i < m_treeModel->columnCount(); ++i) { 0064 m_treeView->hideColumn(i); 0065 } 0066 m_treeView->header()->hide(); 0067 QItemSelectionModel *selectionModel = m_treeView->selectionModel(); 0068 connect(selectionModel, &QItemSelectionModel::currentRowChanged, this, &ProfileWidget::slotChangeSelection); 0069 connect(selectionModel, &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) { 0070 QModelIndex current; 0071 QModelIndex old; 0072 if (!selected.indexes().isEmpty()) { 0073 current = selected.indexes().front(); 0074 } 0075 if (!deselected.indexes().isEmpty()) { 0076 old = deselected.indexes().front(); 0077 } 0078 slotChangeSelection(current, old); 0079 }); 0080 int treeViewFontHeight = QFontInfo(m_treeView->font()).pixelSize(); 0081 m_treeView->setMinimumHeight(treeViewFontHeight); 0082 profileSplitter->addWidget(m_treeView); 0083 m_treeView->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); 0084 m_descriptionPanel = new QTextEdit(this); 0085 m_descriptionPanel->setReadOnly(true); 0086 m_descriptionPanel->viewport()->setCursor(Qt::ArrowCursor); 0087 m_descriptionPanel->viewport()->setBackgroundRole(QPalette::Mid); 0088 m_descriptionPanel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); 0089 m_descriptionPanel->setFrameStyle(QFrame::NoFrame); 0090 m_descriptionPanel->setMinimumHeight(treeViewFontHeight); 0091 profileSplitter->addWidget(m_descriptionPanel); 0092 0093 lay->addWidget(profileSplitter); 0094 profileSplitter->setStretchFactor(0, 4); 0095 profileSplitter->setStretchFactor(1, 3); 0096 0097 refreshFpsCombo(); 0098 auto updateFps = [&]() { 0099 double current = m_fpsFilt->currentData().toDouble(); 0100 KdenliveSettings::setProfile_fps_filter(m_fpsFilt->currentText()); 0101 m_filter->setFilterFps(current > 0, current); 0102 slotFilterChanged(); 0103 }; 0104 connect(m_fpsFilt, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), updateFps); 0105 int ix = m_fpsFilt->findText(KdenliveSettings::profile_fps_filter()); 0106 if (ix > -1) { 0107 m_fpsFilt->setCurrentIndex(ix); 0108 } 0109 m_scanningFilt->addItem(i18n("Any"), -1); 0110 m_scanningFilt->addItem(i18n("Interlaced"), 0); 0111 m_scanningFilt->addItem(i18n("Progressive"), 1); 0112 0113 auto updateScanning = [&]() { 0114 int current = m_scanningFilt->currentData().toInt(); 0115 KdenliveSettings::setProfile_scanning_filter(m_scanningFilt->currentText()); 0116 m_filter->setFilterInterlaced(current != -1, current == 0); 0117 slotFilterChanged(); 0118 }; 0119 connect(m_scanningFilt, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), updateScanning); 0120 0121 ix = m_scanningFilt->findText(KdenliveSettings::profile_scanning_filter()); 0122 if (ix > -1) { 0123 m_scanningFilt->setCurrentIndex(ix); 0124 } 0125 setLayout(lay); 0126 } 0127 0128 ProfileWidget::~ProfileWidget() = default; 0129 0130 void ProfileWidget::refreshFpsCombo() 0131 { 0132 QLocale locale; // Used for UI → OK 0133 QVariant currentValue; 0134 if (m_fpsFilt->count() > 1) { 0135 // remember last selected value 0136 currentValue = m_fpsFilt->currentData(); 0137 } 0138 m_fpsFilt->clear(); 0139 locale.setNumberOptions(QLocale::OmitGroupSeparator); 0140 m_fpsFilt->addItem(i18n("Any"), -1); 0141 auto all_fps = ProfileRepository::get()->getAllFps(); 0142 for (double fps : qAsConst(all_fps)) { 0143 m_fpsFilt->addItem(locale.toString(fps), fps); 0144 } 0145 if (currentValue.isValid()) { 0146 int ix = m_fpsFilt->findData(currentValue); 0147 if (ix > -1) { 0148 m_fpsFilt->setCurrentIndex(ix); 0149 } 0150 } 0151 } 0152 0153 void ProfileWidget::loadProfile(const QString &profile) 0154 { 0155 auto index = m_treeModel->findProfile(profile); 0156 if (index.isValid()) { 0157 m_originalProfile = m_currentProfile = m_lastValidProfile = profile; 0158 if (!trySelectProfile(profile)) { 0159 // When loading a profile, ensure it is visible so reset filters if necessary 0160 m_fpsFilt->setCurrentIndex(0); 0161 m_scanningFilt->setCurrentIndex(0); 0162 } 0163 } 0164 } 0165 0166 const QString ProfileWidget::selectedProfile() const 0167 { 0168 return m_currentProfile; 0169 } 0170 0171 void ProfileWidget::slotEditProfiles() 0172 { 0173 auto *w = new ProfilesDialog(ProfileRepository::get()->getProfile(m_currentProfile)->description()); 0174 w->exec(); 0175 if (w->profileTreeChanged()) { 0176 // Rebuild profiles tree 0177 m_treeModel.reset(); 0178 m_treeModel = ProfileTreeModel::construct(this); 0179 m_filter->setSourceModel(m_treeModel.get()); 0180 refreshFpsCombo(); 0181 loadProfile(m_currentProfile); 0182 } 0183 delete w; 0184 } 0185 0186 void ProfileWidget::fillDescriptionPanel(const QString &profile_path) 0187 { 0188 QString description; 0189 if (profile_path.isEmpty()) { 0190 description += i18n("No profile selected"); 0191 } else { 0192 std::unique_ptr<ProfileModel> &profile = ProfileRepository::get()->getProfile(profile_path); 0193 0194 description += QStringLiteral("<h5>%1</h5>").arg(i18n("Video Settings")); 0195 description += 0196 QStringLiteral("<p style='font-size:small'>%1<br/>") 0197 .arg(i18n("Frame size: %1 x %2 (%3:%4)", profile->width(), profile->height(), profile->display_aspect_num(), profile->display_aspect_den())); 0198 description += i18n("Frame rate: %1 fps", profile->fps()); 0199 description += QStringLiteral("<br/>"); 0200 description += i18n("Pixel aspect ratio: %1", profile->sar()); 0201 description += QStringLiteral("<br/>"); 0202 description += i18n("Color space: %1", profile->colorspaceDescription()); 0203 description += QStringLiteral("<br/>"); 0204 description += i18n("Interlaced: %1", profile->progressive() ? i18n("no") : i18n("yes")); 0205 if (!profile->progressive()) { 0206 description += QStringLiteral("<br/>"); 0207 description += i18n("Field order: %1", profile->bottom_field_first() ? i18n("Bottom field first") : i18n("Top field first")); 0208 } 0209 description += QStringLiteral("</p>"); 0210 } 0211 m_descriptionPanel->setHtml(description); 0212 } 0213 0214 void ProfileWidget::slotChangeSelection(const QModelIndex ¤t, const QModelIndex &previous) 0215 { 0216 auto originalIndex = m_filter->mapToSource(current); 0217 if (m_treeModel->parent(originalIndex) == QModelIndex()) { 0218 // in that case, we have selected a category, which we don't want 0219 QItemSelectionModel *selection = m_treeView->selectionModel(); 0220 selection->select(previous, QItemSelectionModel::Select); 0221 return; 0222 } 0223 m_currentProfile = m_treeModel->getProfile(originalIndex); 0224 if (!m_currentProfile.isEmpty()) { 0225 m_lastValidProfile = m_currentProfile; 0226 } 0227 if (m_originalProfile != m_currentProfile) { 0228 Q_EMIT profileChanged(); 0229 } 0230 fillDescriptionPanel(m_currentProfile); 0231 } 0232 0233 bool ProfileWidget::trySelectProfile(const QString &profile) 0234 { 0235 auto index = m_treeModel->findProfile(profile); 0236 if (index.isValid()) { 0237 // check if element is visible 0238 if (m_filter->isVisible(index)) { 0239 // reselect 0240 QItemSelectionModel *selection = m_treeView->selectionModel(); 0241 selection->select(m_filter->mapFromSource(index), QItemSelectionModel::Select); 0242 // expand corresponding category 0243 auto parent = m_treeModel->parent(index); 0244 m_treeView->expand(m_filter->mapFromSource(parent)); 0245 m_treeView->scrollTo(m_filter->mapFromSource(index), QAbstractItemView::PositionAtCenter); 0246 return true; 0247 } 0248 } 0249 return false; 0250 } 0251 0252 void ProfileWidget::slotFilterChanged() 0253 { 0254 // When filtering change, we must check if the current profile is still visible. 0255 if (!trySelectProfile(m_currentProfile)) { 0256 // we try to back-up the last valid profile 0257 if (!trySelectProfile(m_lastValidProfile)) { 0258 // Everything fails, we don't have any profile 0259 m_currentProfile = QString(); 0260 Q_EMIT profileChanged(); 0261 fillDescriptionPanel(QString()); 0262 } 0263 } 0264 }