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"