Warning, file /libraries/baloo-widgets/src/filemetadatawidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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 void slotLinkActivated(const QString &link); 0047 void slotDataChangeStarted(); 0048 void slotDataChangeFinished(); 0049 0050 QStringList sortedKeys(const QVariantMap &data) const; 0051 QLabel *createLabel(const QString &key, const QString &itemLabel, FileMetaDataWidget *parent); 0052 0053 void saveConfig(); 0054 0055 QList<Row> m_rows; 0056 FileMetaDataProvider *m_provider = nullptr; 0057 QGridLayout *m_gridLayout = nullptr; 0058 0059 MetadataFilter *m_filter = nullptr; 0060 WidgetFactory *m_widgetFactory = nullptr; 0061 0062 QMap<QString, bool> m_visibilityChanged; 0063 bool m_configureVisibleProperties = false; 0064 0065 private: 0066 FileMetaDataWidget *const q; 0067 }; 0068 0069 FileMetaDataWidgetPrivate::FileMetaDataWidgetPrivate(FileMetaDataWidget *parent) 0070 : m_rows() 0071 , q(parent) 0072 { 0073 m_filter = new MetadataFilter(q); 0074 0075 m_widgetFactory = new WidgetFactory(q); 0076 QObject::connect(m_widgetFactory, &WidgetFactory::urlActivated, q, &FileMetaDataWidget::urlActivated); 0077 0078 // TODO: If KFileMetaDataProvider might get a public class in future KDE releases, 0079 // the following code should be moved into KFileMetaDataWidget::setModel(): 0080 m_provider = new FileMetaDataProvider(q); 0081 QObject::connect(m_provider, &FileMetaDataProvider::loadingFinished, q, [this]() { 0082 slotLoadingFinished(); 0083 }); 0084 } 0085 0086 FileMetaDataWidgetPrivate::~FileMetaDataWidgetPrivate() = default; 0087 0088 void FileMetaDataWidgetPrivate::deleteRows() 0089 { 0090 for (const Row &row : std::as_const(m_rows)) { 0091 delete row.label; 0092 row.value->deleteLater(); 0093 if (row.checkBox) { 0094 row.checkBox->deleteLater(); 0095 } 0096 } 0097 0098 m_rows.clear(); 0099 } 0100 0101 QLabel *FileMetaDataWidgetPrivate::createLabel(const QString &key, const QString &itemLabel, FileMetaDataWidget *parent) 0102 { 0103 auto label = new QLabel(itemLabel + QLatin1Char(':'), parent); 0104 label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); 0105 label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); 0106 label->setForegroundRole(parent->foregroundRole()); 0107 label->setFont(parent->font()); 0108 label->setWordWrap(true); 0109 label->setAlignment(Qt::AlignTop | Qt::AlignRight); 0110 label->setObjectName(QStringLiteral("L_%1").arg(key)); 0111 return label; 0112 } 0113 0114 void FileMetaDataWidgetPrivate::slotLoadingFinished() 0115 { 0116 deleteRows(); 0117 0118 if (m_gridLayout == nullptr) { 0119 m_gridLayout = new QGridLayout(q); 0120 m_gridLayout->setContentsMargins(0, 0, 0, 0); 0121 m_gridLayout->setSpacing(q->fontMetrics().height() / 4); 0122 } 0123 0124 QVariantMap data = m_provider->data(); 0125 QStringList active; 0126 if (m_configureVisibleProperties) { 0127 active = m_filter->filter(data).keys(); 0128 auto changedIt = m_visibilityChanged.constBegin(); 0129 while (changedIt != m_visibilityChanged.constEnd()) { 0130 if (changedIt.value()) { 0131 active.append(changedIt.key()); 0132 } else { 0133 active.removeAll(changedIt.key()); 0134 } 0135 changedIt++; 0136 } 0137 m_widgetFactory->setReadOnly(true); 0138 m_gridLayout->setColumnStretch(0, 1); 0139 m_gridLayout->setColumnStretch(1, 3); 0140 m_gridLayout->setColumnStretch(2, 0); 0141 m_gridLayout->setColumnStretch(3, 6); 0142 } else { 0143 data = m_filter->filter(data); 0144 m_widgetFactory->setReadOnly(m_provider->isReadOnly()); 0145 m_gridLayout->setColumnStretch(0, 4); 0146 m_gridLayout->setColumnStretch(1, 0); 0147 m_gridLayout->setColumnStretch(2, 6); 0148 m_gridLayout->setColumnStretch(3, 0); 0149 } 0150 0151 int rowIndex = 0; 0152 // Iterate through all remaining items. 0153 // Embed the label and the value as new row in the widget 0154 const QStringList keys = sortedKeys(data); 0155 const int spacerWidth = QFontMetrics(q->font()).size(Qt::TextSingleLine, QStringLiteral(" ")).width(); 0156 0157 const int labelColumn = m_configureVisibleProperties ? 1 : 0; 0158 0159 for (const auto &key : keys) { 0160 Row row; 0161 if (m_configureVisibleProperties) { 0162 row.checkBox = new QCheckBox(q); 0163 if (active.contains(key)) { 0164 row.checkBox->setChecked(true); 0165 } 0166 m_gridLayout->addWidget(row.checkBox, rowIndex, 0, Qt::AlignTop | Qt::AlignRight); 0167 QObject::connect(row.checkBox, &QCheckBox::stateChanged, q, [this, key](int state) { 0168 this->m_visibilityChanged[key] = (state == Qt::Checked); 0169 }); 0170 } else { 0171 row.checkBox = nullptr; 0172 } 0173 0174 row.label = createLabel(key, m_provider->label(key), q); 0175 m_gridLayout->addWidget(row.label, rowIndex, labelColumn + 0, Qt::AlignRight); 0176 0177 m_gridLayout->addItem(new QSpacerItem(spacerWidth, 1), rowIndex, labelColumn + 1); 0178 0179 row.value = m_widgetFactory->createWidget(key, data[key], q); 0180 m_gridLayout->addWidget(row.value, rowIndex, labelColumn + 2, Qt::AlignLeft); 0181 0182 m_gridLayout->setRowStretch(rowIndex, 0); 0183 0184 // Remember the label and value-widget as row 0185 m_rows.append(row); 0186 ++rowIndex; 0187 } 0188 0189 // Add vertical stretch - when the widget is embedded with extra vertical 0190 // space, it should be added at the bottom, not distributed between the 0191 // items. 0192 m_gridLayout->addItem(new QSpacerItem(0, 0), rowIndex, 0, 1, -1); 0193 m_gridLayout->setRowStretch(rowIndex, 1); 0194 0195 q->updateGeometry(); 0196 Q_EMIT q->metaDataRequestFinished(m_provider->items()); 0197 } 0198 0199 void FileMetaDataWidgetPrivate::slotLinkActivated(const QString &link) 0200 { 0201 const QUrl url = QUrl::fromUserInput(link); 0202 if (url.isValid()) { 0203 Q_EMIT q->urlActivated(url); 0204 } 0205 } 0206 0207 void FileMetaDataWidgetPrivate::slotDataChangeStarted() 0208 { 0209 q->setEnabled(false); 0210 } 0211 0212 void FileMetaDataWidgetPrivate::slotDataChangeFinished() 0213 { 0214 q->setEnabled(true); 0215 } 0216 0217 QStringList FileMetaDataWidgetPrivate::sortedKeys(const QVariantMap &data) const 0218 { 0219 // Create a map, where the translated label prefixed with the 0220 // sort priority acts as key. The data of each entry is the URI 0221 // of the data. By this the all URIs are sorted by the sort priority 0222 // and sub sorted by the translated labels. 0223 QMap<QString, QString> map; 0224 QVariantMap::const_iterator hashIt = data.constBegin(); 0225 while (hashIt != data.constEnd()) { 0226 const QString propName = hashIt.key(); 0227 0228 QString key = m_provider->group(propName); 0229 key += m_provider->label(propName); 0230 0231 map.insert(key, propName); 0232 ++hashIt; 0233 } 0234 0235 // Apply the URIs from the map to the list that will get returned. 0236 // The list will then be alphabetically ordered by the translated labels of the URIs. 0237 QStringList list; 0238 QMap<QString, QString>::const_iterator mapIt = map.constBegin(); 0239 while (mapIt != map.constEnd()) { 0240 list.append(mapIt.value()); 0241 ++mapIt; 0242 } 0243 0244 return list; 0245 } 0246 0247 void FileMetaDataWidgetPrivate::saveConfig() 0248 { 0249 if (m_visibilityChanged.isEmpty()) { 0250 return; 0251 } 0252 0253 KConfig config(QStringLiteral("baloofileinformationrc"), KConfig::NoGlobals); 0254 KConfigGroup showGroup = config.group("Show"); 0255 0256 auto changedIt = m_visibilityChanged.constBegin(); 0257 while (changedIt != m_visibilityChanged.constEnd()) { 0258 showGroup.writeEntry(changedIt.key(), changedIt.value()); 0259 changedIt++; 0260 } 0261 0262 showGroup.sync(); 0263 } 0264 0265 FileMetaDataWidget::FileMetaDataWidget(QWidget *parent) 0266 : QWidget(parent) 0267 , d(new FileMetaDataWidgetPrivate(this)) 0268 { 0269 } 0270 0271 FileMetaDataWidget::~FileMetaDataWidget() = default; 0272 0273 void FileMetaDataWidget::setItems(const KFileItemList &items) 0274 { 0275 d->m_provider->setItems(items); 0276 d->m_widgetFactory->setItems(items); 0277 } 0278 0279 KFileItemList FileMetaDataWidget::items() const 0280 { 0281 return d->m_provider->items(); 0282 } 0283 0284 void FileMetaDataWidget::setReadOnly(bool readOnly) 0285 { 0286 d->m_provider->setReadOnly(readOnly); 0287 d->m_widgetFactory->setReadOnly(readOnly); 0288 } 0289 0290 bool FileMetaDataWidget::isReadOnly() const 0291 { 0292 return d->m_provider->isReadOnly(); 0293 } 0294 void FileMetaDataWidget::setDateFormat(const DateFormats format) 0295 { 0296 d->m_widgetFactory->setDateFormat(format); 0297 } 0298 0299 DateFormats FileMetaDataWidget::dateFormat() const 0300 { 0301 return d->m_widgetFactory->dateFormat(); 0302 } 0303 0304 QSize FileMetaDataWidget::sizeHint() const 0305 { 0306 if (d->m_gridLayout == nullptr) { 0307 return QWidget::sizeHint(); 0308 } 0309 0310 // Calculate the required width for the labels and values 0311 int leftWidthMax = 0; 0312 int rightWidthMax = 0; 0313 int rightWidthAverage = 0; 0314 for (const FileMetaDataWidgetPrivate::Row &row : std::as_const(d->m_rows)) { 0315 const QWidget *valueWidget = row.value; 0316 const int rightWidth = valueWidget->sizeHint().width(); 0317 rightWidthAverage += rightWidth; 0318 if (rightWidth > rightWidthMax) { 0319 rightWidthMax = rightWidth; 0320 } 0321 0322 const int leftWidth = row.label->sizeHint().width(); 0323 if (leftWidth > leftWidthMax) { 0324 leftWidthMax = leftWidth; 0325 } 0326 } 0327 0328 // Some value widgets might return a very huge width for the size hint. 0329 // Limit the maximum width to the double width of the overall average 0330 // to assure a less messed layout. 0331 if (d->m_rows.count() > 1) { 0332 rightWidthAverage /= d->m_rows.count(); 0333 if (rightWidthMax > rightWidthAverage * 2) { 0334 rightWidthMax = rightWidthAverage * 2; 0335 } 0336 } 0337 0338 // Based on the available width calculate the required height 0339 int height = getMargin() * 2 + d->m_gridLayout->spacing() * (d->m_rows.count() - 1); 0340 for (const FileMetaDataWidgetPrivate::Row &row : std::as_const(d->m_rows)) { 0341 const QWidget *valueWidget = row.value; 0342 const int rowHeight = qMax(row.label->heightForWidth(leftWidthMax), valueWidget->heightForWidth(rightWidthMax)); 0343 height += rowHeight; 0344 } 0345 0346 const int width = getMargin() * 2 + leftWidthMax + d->m_gridLayout->spacing() + rightWidthMax; 0347 0348 return QSize{width, height}; 0349 } 0350 0351 void FileMetaDataWidget::setConfigurationMode(ConfigurationMode mode) 0352 { 0353 if (mode == ConfigurationMode::ReStart) { 0354 d->m_configureVisibleProperties = true; 0355 } else if (mode == ConfigurationMode::Accept) { 0356 d->saveConfig(); 0357 d->m_configureVisibleProperties = false; 0358 } else if (mode == ConfigurationMode::Cancel) { 0359 d->m_configureVisibleProperties = false; 0360 } 0361 d->m_visibilityChanged.clear(); 0362 d->slotLoadingFinished(); 0363 } 0364 0365 int FileMetaDataWidget::getMargin() const 0366 { 0367 int left, top, right, bottom; 0368 d->m_gridLayout->getContentsMargins(&left, &top, &right, &bottom); 0369 if (left == top && top == right && right == bottom) { 0370 return left; 0371 } else { 0372 return -1; 0373 } 0374 } 0375 0376 #include "moc_filemetadatawidget.cpp"