File indexing completed on 2024-04-21 04:39:56
0001 /* 0002 SPDX-FileCopyrightText: 2012-2013 Vishesh Handa <me@vhanda.in> 0003 0004 Adapted from KFileMetadataWidget 0005 SPDX-FileCopyrightText: 2008 Sebastian Trueg <trueg@kde.org> 0006 SPDX-FileCopyrightText: 2009-2010 Peter Penz <peter.penz@gmx.at> 0007 0008 SPDX-License-Identifier: LGPL-2.1-or-later 0009 */ 0010 0011 #include "filemetadatawidget.h" 0012 #include "filemetadataprovider.h" 0013 #include "metadatafilter.h" 0014 #include "widgetfactory.h" 0015 0016 #include <KConfig> 0017 #include <KConfigGroup> 0018 0019 #include <QCheckBox> 0020 #include <QGridLayout> 0021 #include <QLabel> 0022 #include <QList> 0023 #include <QSpacerItem> 0024 #include <QString> 0025 0026 using namespace Baloo; 0027 0028 class Baloo::FileMetaDataWidgetPrivate 0029 { 0030 public: 0031 struct Row { 0032 QCheckBox *checkBox; 0033 QLabel *label; 0034 QWidget *value; 0035 }; 0036 0037 explicit FileMetaDataWidgetPrivate(FileMetaDataWidget *parent); 0038 ~FileMetaDataWidgetPrivate(); 0039 0040 FileMetaDataWidgetPrivate(const FileMetaDataWidgetPrivate&) = delete; 0041 FileMetaDataWidget& operator=(const FileMetaDataWidgetPrivate&) = delete; 0042 0043 void deleteRows(); 0044 0045 void slotLoadingFinished(); 0046 0047 QStringList sortedKeys(const QVariantMap &data) const; 0048 QLabel *createLabel(const QString &key, const QString &itemLabel, FileMetaDataWidget *parent); 0049 0050 void saveConfig(); 0051 0052 QList<Row> m_rows; 0053 FileMetaDataProvider *m_provider = nullptr; 0054 QGridLayout *m_gridLayout = nullptr; 0055 0056 MetadataFilter *m_filter = nullptr; 0057 WidgetFactory *m_widgetFactory = nullptr; 0058 0059 QMap<QString, bool> m_visibilityChanged; 0060 bool m_configureVisibleProperties = false; 0061 0062 private: 0063 FileMetaDataWidget *const q; 0064 }; 0065 0066 FileMetaDataWidgetPrivate::FileMetaDataWidgetPrivate(FileMetaDataWidget *parent) 0067 : m_rows() 0068 , q(parent) 0069 { 0070 m_filter = new MetadataFilter(q); 0071 0072 m_widgetFactory = new WidgetFactory(q); 0073 QObject::connect(m_widgetFactory, &WidgetFactory::urlActivated, q, &FileMetaDataWidget::urlActivated); 0074 0075 // TODO: If KFileMetaDataProvider might get a public class in future KDE releases, 0076 // the following code should be moved into KFileMetaDataWidget::setModel(): 0077 m_provider = new FileMetaDataProvider(q); 0078 QObject::connect(m_provider, &FileMetaDataProvider::loadingFinished, q, [this]() { 0079 slotLoadingFinished(); 0080 }); 0081 } 0082 0083 FileMetaDataWidgetPrivate::~FileMetaDataWidgetPrivate() = default; 0084 0085 void FileMetaDataWidgetPrivate::deleteRows() 0086 { 0087 for (const Row &row : std::as_const(m_rows)) { 0088 delete row.label; 0089 row.value->deleteLater(); 0090 if (row.checkBox) { 0091 row.checkBox->deleteLater(); 0092 } 0093 } 0094 0095 m_rows.clear(); 0096 } 0097 0098 QLabel *FileMetaDataWidgetPrivate::createLabel(const QString &key, const QString &itemLabel, FileMetaDataWidget *parent) 0099 { 0100 auto label = new QLabel(itemLabel + QLatin1Char(':'), parent); 0101 label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); 0102 label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); 0103 label->setForegroundRole(parent->foregroundRole()); 0104 label->setFont(parent->font()); 0105 label->setWordWrap(true); 0106 label->setAlignment(Qt::AlignTop | Qt::AlignRight); 0107 label->setObjectName(QStringLiteral("L_%1").arg(key)); 0108 return label; 0109 } 0110 0111 void FileMetaDataWidgetPrivate::slotLoadingFinished() 0112 { 0113 deleteRows(); 0114 0115 if (m_gridLayout == nullptr) { 0116 m_gridLayout = new QGridLayout(q); 0117 m_gridLayout->setContentsMargins(0, 0, 0, 0); 0118 m_gridLayout->setSpacing(q->fontMetrics().height() / 4); 0119 } 0120 0121 QVariantMap data = m_provider->data(); 0122 QStringList active; 0123 if (m_configureVisibleProperties) { 0124 active = m_filter->filter(data).keys(); 0125 auto changedIt = m_visibilityChanged.constBegin(); 0126 while (changedIt != m_visibilityChanged.constEnd()) { 0127 if (changedIt.value()) { 0128 active.append(changedIt.key()); 0129 } else { 0130 active.removeAll(changedIt.key()); 0131 } 0132 changedIt++; 0133 } 0134 m_widgetFactory->setReadOnly(true); 0135 m_gridLayout->setColumnStretch(0, 1); 0136 m_gridLayout->setColumnStretch(1, 3); 0137 m_gridLayout->setColumnStretch(2, 0); 0138 m_gridLayout->setColumnStretch(3, 6); 0139 } else { 0140 data = m_filter->filter(data); 0141 m_widgetFactory->setReadOnly(m_provider->isReadOnly()); 0142 m_gridLayout->setColumnStretch(0, 4); 0143 m_gridLayout->setColumnStretch(1, 0); 0144 m_gridLayout->setColumnStretch(2, 6); 0145 m_gridLayout->setColumnStretch(3, 0); 0146 } 0147 0148 int rowIndex = 0; 0149 // Iterate through all remaining items. 0150 // Embed the label and the value as new row in the widget 0151 const QStringList keys = sortedKeys(data); 0152 const int spacerWidth = QFontMetrics(q->font()).size(Qt::TextSingleLine, QStringLiteral(" ")).width(); 0153 0154 const int labelColumn = m_configureVisibleProperties ? 1 : 0; 0155 0156 for (const auto &key : keys) { 0157 Row row; 0158 if (m_configureVisibleProperties) { 0159 row.checkBox = new QCheckBox(q); 0160 if (active.contains(key)) { 0161 row.checkBox->setChecked(true); 0162 } 0163 m_gridLayout->addWidget(row.checkBox, rowIndex, 0, Qt::AlignTop | Qt::AlignRight); 0164 QObject::connect(row.checkBox, &QCheckBox::stateChanged, q, [this, key](int state) { 0165 this->m_visibilityChanged[key] = (state == Qt::Checked); 0166 }); 0167 } else { 0168 row.checkBox = nullptr; 0169 } 0170 0171 row.label = createLabel(key, m_provider->label(key), q); 0172 m_gridLayout->addWidget(row.label, rowIndex, labelColumn + 0, Qt::AlignRight); 0173 0174 m_gridLayout->addItem(new QSpacerItem(spacerWidth, 1), rowIndex, labelColumn + 1); 0175 0176 row.value = m_widgetFactory->createWidget(key, data[key], q); 0177 m_gridLayout->addWidget(row.value, rowIndex, labelColumn + 2, Qt::AlignLeft); 0178 0179 m_gridLayout->setRowStretch(rowIndex, 0); 0180 0181 // Remember the label and value-widget as row 0182 m_rows.append(row); 0183 ++rowIndex; 0184 } 0185 0186 // Add vertical stretch - when the widget is embedded with extra vertical 0187 // space, it should be added at the bottom, not distributed between the 0188 // items. 0189 m_gridLayout->addItem(new QSpacerItem(0, 0), rowIndex, 0, 1, -1); 0190 m_gridLayout->setRowStretch(rowIndex, 1); 0191 0192 q->updateGeometry(); 0193 Q_EMIT q->metaDataRequestFinished(m_provider->items()); 0194 } 0195 0196 QStringList FileMetaDataWidgetPrivate::sortedKeys(const QVariantMap &data) const 0197 { 0198 // Create a map, where the translated label prefixed with the 0199 // sort priority acts as key. The data of each entry is the URI 0200 // of the data. By this the all URIs are sorted by the sort priority 0201 // and sub sorted by the translated labels. 0202 QMap<QString, QString> map; 0203 QVariantMap::const_iterator hashIt = data.constBegin(); 0204 while (hashIt != data.constEnd()) { 0205 const QString propName = hashIt.key(); 0206 0207 QString key = m_provider->group(propName); 0208 key += m_provider->label(propName); 0209 0210 map.insert(key, propName); 0211 ++hashIt; 0212 } 0213 0214 // Apply the URIs from the map to the list that will get returned. 0215 // The list will then be alphabetically ordered by the translated labels of the URIs. 0216 QStringList list; 0217 QMap<QString, QString>::const_iterator mapIt = map.constBegin(); 0218 while (mapIt != map.constEnd()) { 0219 list.append(mapIt.value()); 0220 ++mapIt; 0221 } 0222 0223 return list; 0224 } 0225 0226 void FileMetaDataWidgetPrivate::saveConfig() 0227 { 0228 if (m_visibilityChanged.isEmpty()) { 0229 return; 0230 } 0231 0232 KConfig config(QStringLiteral("baloofileinformationrc"), KConfig::NoGlobals); 0233 KConfigGroup showGroup = config.group(QStringLiteral("Show")); 0234 0235 auto changedIt = m_visibilityChanged.constBegin(); 0236 while (changedIt != m_visibilityChanged.constEnd()) { 0237 showGroup.writeEntry(changedIt.key(), changedIt.value()); 0238 changedIt++; 0239 } 0240 0241 showGroup.sync(); 0242 } 0243 0244 FileMetaDataWidget::FileMetaDataWidget(QWidget *parent) 0245 : QWidget(parent) 0246 , d(new FileMetaDataWidgetPrivate(this)) 0247 { 0248 } 0249 0250 FileMetaDataWidget::~FileMetaDataWidget() = default; 0251 0252 void FileMetaDataWidget::setItems(const KFileItemList &items) 0253 { 0254 d->m_provider->setItems(items); 0255 d->m_widgetFactory->setItems(items); 0256 } 0257 0258 KFileItemList FileMetaDataWidget::items() const 0259 { 0260 return d->m_provider->items(); 0261 } 0262 0263 void FileMetaDataWidget::setReadOnly(bool readOnly) 0264 { 0265 d->m_provider->setReadOnly(readOnly); 0266 d->m_widgetFactory->setReadOnly(readOnly); 0267 } 0268 0269 bool FileMetaDataWidget::isReadOnly() const 0270 { 0271 return d->m_provider->isReadOnly(); 0272 } 0273 void FileMetaDataWidget::setDateFormat(const DateFormats format) 0274 { 0275 d->m_widgetFactory->setDateFormat(format); 0276 } 0277 0278 DateFormats FileMetaDataWidget::dateFormat() const 0279 { 0280 return d->m_widgetFactory->dateFormat(); 0281 } 0282 0283 QSize FileMetaDataWidget::sizeHint() const 0284 { 0285 if (d->m_gridLayout == nullptr) { 0286 return QWidget::sizeHint(); 0287 } 0288 0289 // Calculate the required width for the labels and values 0290 int leftWidthMax = 0; 0291 int rightWidthMax = 0; 0292 int rightWidthAverage = 0; 0293 for (const FileMetaDataWidgetPrivate::Row &row : std::as_const(d->m_rows)) { 0294 const QWidget *valueWidget = row.value; 0295 const int rightWidth = valueWidget->sizeHint().width(); 0296 rightWidthAverage += rightWidth; 0297 if (rightWidth > rightWidthMax) { 0298 rightWidthMax = rightWidth; 0299 } 0300 0301 const int leftWidth = row.label->sizeHint().width(); 0302 if (leftWidth > leftWidthMax) { 0303 leftWidthMax = leftWidth; 0304 } 0305 } 0306 0307 // Some value widgets might return a very huge width for the size hint. 0308 // Limit the maximum width to the double width of the overall average 0309 // to assure a less messed layout. 0310 if (d->m_rows.count() > 1) { 0311 rightWidthAverage /= d->m_rows.count(); 0312 if (rightWidthMax > rightWidthAverage * 2) { 0313 rightWidthMax = rightWidthAverage * 2; 0314 } 0315 } 0316 0317 // Based on the available width calculate the required height 0318 const auto margins = d->m_gridLayout->contentsMargins(); 0319 int height = margins.top() + margins.bottom() + d->m_gridLayout->spacing() * (d->m_rows.count() - 1); 0320 for (const FileMetaDataWidgetPrivate::Row &row : std::as_const(d->m_rows)) { 0321 const QWidget *valueWidget = row.value; 0322 const int rowHeight = qMax(row.label->heightForWidth(leftWidthMax), valueWidget->heightForWidth(rightWidthMax)); 0323 height += rowHeight; 0324 } 0325 0326 const int width = margins.left() + margins.right() + leftWidthMax + d->m_gridLayout->spacing() + rightWidthMax; 0327 0328 return QSize{width, height}; 0329 } 0330 0331 void FileMetaDataWidget::setConfigurationMode(ConfigurationMode mode) 0332 { 0333 if (mode == ConfigurationMode::ReStart) { 0334 d->m_configureVisibleProperties = true; 0335 } else if (mode == ConfigurationMode::Accept) { 0336 d->saveConfig(); 0337 d->m_configureVisibleProperties = false; 0338 } else if (mode == ConfigurationMode::Cancel) { 0339 d->m_configureVisibleProperties = false; 0340 } 0341 d->m_visibilityChanged.clear(); 0342 d->slotLoadingFinished(); 0343 } 0344 0345 #include "moc_filemetadatawidget.cpp"