File indexing completed on 2024-05-05 16:08:26

0001 /*****************************************************************************
0002  * Copyright (C) 2008-2010 by Sebastian Trueg <trueg@kde.org>                *
0003  * Copyright (C) 2009-2010 by Peter Penz <peter.penz@gmx.at>                 *
0004  *                                                                           *
0005  * This library is free software; you can redistribute it and/or             *
0006  * modify it under the terms of the GNU Library General Public               *
0007  * License as published by the Free Software Foundation; either              *
0008  * version 2 of the License, or (at your option) any later version.          *
0009  *                                                                           *
0010  * This library is distributed in the hope that it will be useful,           *
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
0013  * Library General Public License for more details.                          *
0014  *                                                                           *
0015  * You should have received a copy of the GNU Library General Public License *
0016  * along with this library; see the file COPYING.LIB.  If not, write to      *
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,      *
0018  * Boston, MA 02110-1301, USA.                                               *
0019  *****************************************************************************/
0020 
0021 #include "kfilemetadatawidget.h"
0022 
0023 #include <kconfig.h>
0024 #include <kconfiggroup.h>
0025 #include <klocalizedstring.h>
0026 
0027 #include <QGridLayout>
0028 #include <QLabel>
0029 #include <QSet>
0030 #include <QString>
0031 #include <QTimer>
0032 
0033 #include <config-kdelibs4support.h>
0034 #if ! KIO_NO_NEPOMUK
0035 #define DISABLE_NEPOMUK_LEGACY
0036 
0037 #include <property.h>
0038 #include <tag.h>
0039 
0040 #include <QSpacerItem>
0041 
0042 #include "kfilemetadataprovider_p.h"
0043 #endif
0044 
0045 class Q_DECL_HIDDEN KFileMetaDataWidget::Private
0046 {
0047 public:
0048     struct Row {
0049         QLabel *label;
0050         QWidget *value;
0051     };
0052 
0053     Private(KFileMetaDataWidget *parent);
0054     ~Private();
0055 
0056     /**
0057      * Initializes the configuration file "kmetainformationrc"
0058      * with proper default settings for the first start in
0059      * an uninitialized environment.
0060      */
0061     void initMetaInfoSettings();
0062 
0063     /**
0064      * Parses the configuration file "kmetainformationrc" and
0065      * updates the visibility of all rows that got their data
0066      * from KFileItem.
0067      */
0068     void updateFileItemRowsVisibility();
0069 
0070     void deleteRows();
0071 
0072     void slotLoadingFinished();
0073     void slotLinkActivated(const QString &link);
0074     void slotDataChangeStarted();
0075     void slotDataChangeFinished();
0076 
0077 #if ! KIO_NO_NEPOMUK
0078     QList<QUrl> sortedKeys(const QHash<QUrl, Nepomuk::Variant> &data) const;
0079 
0080     /**
0081      * @return True, if at least one of the file items \a m_fileItems has
0082      *         a valid Nepomuk URI.
0083      */
0084     bool hasNepomukUris() const;
0085 #endif
0086 
0087     QList<Row> m_rows;
0088 #if ! KIO_NO_NEPOMUK
0089     KFileMetaDataProvider *m_provider;
0090 #endif
0091     QGridLayout *m_gridLayout;
0092 
0093 private:
0094     KFileMetaDataWidget *const q;
0095 };
0096 
0097 KFileMetaDataWidget::Private::Private(KFileMetaDataWidget *parent) :
0098     m_rows(),
0099 #if ! KIO_NO_NEPOMUK
0100     m_provider(0),
0101 #endif
0102     m_gridLayout(nullptr),
0103     q(parent)
0104 {
0105     initMetaInfoSettings();
0106 
0107 #if ! KIO_NO_NEPOMUK
0108     // TODO: If KFileMetaDataProvider might get a public class in future KDE releases,
0109     // the following code should be moved into KFileMetaDataWidget::setModel():
0110     m_provider = new KFileMetaDataProvider(q);
0111     connect(m_provider, SIGNAL(loadingFinished()), q, SLOT(slotLoadingFinished()));
0112     connect(m_provider, SIGNAL(urlActivated(QUrl)), q, SIGNAL(urlActivated(QUrl)));
0113 #endif
0114 }
0115 
0116 KFileMetaDataWidget::Private::~Private()
0117 {
0118 }
0119 
0120 void KFileMetaDataWidget::Private::initMetaInfoSettings()
0121 {
0122     const int currentVersion = 3; // increase version, if the blacklist of disabled
0123     // properties should be updated
0124 
0125     KConfig config("kmetainformationrc", KConfig::NoGlobals);
0126     if (config.group("Misc").readEntry("version", 0) < currentVersion) {
0127         // The resource file is read the first time. Assure
0128         // that some meta information is disabled per default.
0129 
0130         // clear old info
0131         config.deleteGroup("Show");
0132         KConfigGroup settings = config.group("Show");
0133 
0134         static const char *const disabledProperties[] = {
0135             "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#comment",
0136             "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#contentSize",
0137             "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#depends",
0138             "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#isPartOf",
0139             "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#lastModified",
0140             "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#mimeType",
0141             "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#plainTextContent",
0142             "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#url",
0143             "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#averageBitrate",
0144             "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#channels",
0145             "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#fileName",
0146             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#apertureValue",
0147             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#exposureBiasValue",
0148             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#exposureTime",
0149             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#flash",
0150             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#focalLength",
0151             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#focalLengthIn35mmFilm",
0152             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#isoSpeedRatings",
0153             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#make",
0154             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#meteringMode",
0155             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#model",
0156             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#orientation",
0157             "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#whiteBalance",
0158             "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#description",
0159             "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#hasTag",
0160             "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#lastModified",
0161             "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#numericRating",
0162             "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
0163             "kfileitem#owner",
0164             "kfileitem#permissions",
0165             nullptr // mandatory last entry
0166         };
0167 
0168         for (int i = 0; disabledProperties[i] != nullptr; ++i) {
0169             settings.writeEntry(disabledProperties[i], false);
0170         }
0171 
0172         // mark the group as initialized
0173         config.group("Misc").writeEntry("version", currentVersion);
0174     }
0175 }
0176 
0177 void KFileMetaDataWidget::Private::deleteRows()
0178 {
0179     foreach (const Row &row, m_rows) {
0180         delete row.label;
0181         delete row.value;
0182     }
0183     m_rows.clear();
0184 }
0185 
0186 void KFileMetaDataWidget::Private::slotLoadingFinished()
0187 {
0188 #if ! KIO_NO_NEPOMUK
0189     deleteRows();
0190 
0191     if (!hasNepomukUris()) {
0192         q->updateGeometry();
0193         emit q->metaDataRequestFinished(m_provider->items());
0194         return;
0195     }
0196 
0197     if (m_gridLayout == 0) {
0198         m_gridLayout = new QGridLayout(q);
0199         m_gridLayout->setContentsMargins(0, 0, 0, 0);
0200         m_gridLayout->setSpacing(q->fontMetrics().height() / 4);
0201     }
0202 
0203     QHash<QUrl, Nepomuk::Variant> data = m_provider->data();
0204 
0205     // Remove all items, that are marked as hidden in kmetainformationrc
0206     KConfig config("kmetainformationrc", KConfig::NoGlobals);
0207     KConfigGroup settings = config.group("Show");
0208     QHash<QUrl, Nepomuk::Variant>::iterator it = data.begin();
0209     while (it != data.end()) {
0210         const QString uriString = it.key().url();
0211         if (!settings.readEntry(uriString, true) ||
0212                 !Nepomuk::Types::Property(it.key()).userVisible()) {
0213             it = data.erase(it);
0214         } else {
0215             ++it;
0216         }
0217     }
0218 
0219     // Iterate through all remaining items embed the label
0220     // and the value as new row in the widget
0221     int rowIndex = 0;
0222     const QList<QUrl> keys = sortedKeys(data);
0223     foreach (const QUrl &key, keys) {
0224         const Nepomuk::Variant value = data[key];
0225         QString itemLabel = m_provider->label(key);
0226         itemLabel.append(QLatin1Char(':'));
0227 
0228         // Create label
0229         QLabel *label = new QLabel(itemLabel, q);
0230         label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
0231         label->setForegroundRole(q->foregroundRole());
0232         label->setFont(q->font());
0233         label->setWordWrap(true);
0234         label->setAlignment(Qt::AlignTop | Qt::AlignRight);
0235 
0236         // Create value-widget
0237         QWidget *valueWidget = m_provider->createValueWidget(key, value, q);
0238 
0239         // Add the label and value-widget to grid layout
0240         m_gridLayout->addWidget(label, rowIndex, 0, Qt::AlignRight);
0241         const int spacerWidth = QFontMetrics(q->font()).size(Qt::TextSingleLine, " ").width();
0242         m_gridLayout->addItem(new QSpacerItem(spacerWidth, 1), rowIndex, 1);
0243         m_gridLayout->addWidget(valueWidget, rowIndex, 2, Qt::AlignLeft);
0244 
0245         // Remember the label and value-widget as row
0246         Row row;
0247         row.label = label;
0248         row.value = valueWidget;
0249         m_rows.append(row);
0250 
0251         ++rowIndex;
0252     }
0253 #endif
0254 
0255     q->updateGeometry();
0256 #if ! KIO_NO_NEPOMUK
0257     emit q->metaDataRequestFinished(m_provider->items());
0258 #endif
0259 }
0260 
0261 void KFileMetaDataWidget::Private::slotLinkActivated(const QString &link)
0262 {
0263     const QUrl url(link);
0264     if (url.isValid()) {
0265         emit q->urlActivated(url);
0266     }
0267 }
0268 
0269 void KFileMetaDataWidget::Private::slotDataChangeStarted()
0270 {
0271     q->setEnabled(false);
0272 }
0273 
0274 void KFileMetaDataWidget::Private::slotDataChangeFinished()
0275 {
0276     q->setEnabled(true);
0277 }
0278 
0279 #if ! KIO_NO_NEPOMUK
0280 QList<QUrl> KFileMetaDataWidget::Private::sortedKeys(const QHash<QUrl, Nepomuk::Variant> &data) const
0281 {
0282     // Create a map, where the translated label prefixed with the
0283     // sort priority acts as key. The data of each entry is the URI
0284     // of the data. By this the all URIs are sorted by the sort priority
0285     // and sub sorted by the translated labels.
0286     QMap<QString, QUrl> map;
0287     QHash<QUrl, Nepomuk::Variant>::const_iterator hashIt = data.constBegin();
0288     while (hashIt != data.constEnd()) {
0289         const QUrl uri = hashIt.key();
0290 
0291         QString key = m_provider->group(uri);
0292         key += m_provider->label(uri);
0293 
0294         map.insert(key, uri);
0295         ++hashIt;
0296     }
0297 
0298     // Apply the URIs from the map to the list that will get returned.
0299     // The list will then be alphabetically ordered by the translated labels of the URIs.
0300     QList<QUrl> list;
0301     QMap<QString, QUrl>::const_iterator mapIt = map.constBegin();
0302     while (mapIt != map.constEnd()) {
0303         list.append(mapIt.value());
0304         ++mapIt;
0305     }
0306 
0307     return list;
0308 }
0309 
0310 bool KFileMetaDataWidget::Private::hasNepomukUris() const
0311 {
0312     foreach (const KFileItem &fileItem, m_provider->items()) {
0313         if (fileItem.nepomukUri().isValid()) {
0314             return true;
0315         }
0316     }
0317     return false;
0318 }
0319 #endif
0320 
0321 KFileMetaDataWidget::KFileMetaDataWidget(QWidget *parent) :
0322     QWidget(parent),
0323     d(new Private(this))
0324 {
0325 }
0326 
0327 KFileMetaDataWidget::~KFileMetaDataWidget()
0328 {
0329     delete d;
0330 }
0331 
0332 void KFileMetaDataWidget::setItems(const KFileItemList &items)
0333 {
0334 #if KIO_NO_NEPOMUK
0335     Q_UNUSED(items)
0336 #else
0337     d->m_provider->setItems(items);
0338 #endif
0339 }
0340 
0341 KFileItemList KFileMetaDataWidget::items() const
0342 {
0343 #if ! KIO_NO_NEPOMUK
0344     return d->m_provider->items();
0345 #else
0346     return KFileItemList();
0347 #endif
0348 }
0349 
0350 void KFileMetaDataWidget::setReadOnly(bool readOnly)
0351 {
0352 #if KIO_NO_NEPOMUK
0353     Q_UNUSED(readOnly)
0354 #else
0355     d->m_provider->setReadOnly(readOnly);
0356 #endif
0357 }
0358 
0359 bool KFileMetaDataWidget::isReadOnly() const
0360 {
0361 #if ! KIO_NO_NEPOMUK
0362     return d->m_provider->isReadOnly();
0363 #else
0364     return true;
0365 #endif
0366 }
0367 
0368 QSize KFileMetaDataWidget::sizeHint() const
0369 {
0370     if (d->m_gridLayout == nullptr) {
0371         return QWidget::sizeHint();
0372     }
0373 
0374     // Calculate the required width for the labels and values
0375     int leftWidthMax = 0;
0376     int rightWidthMax = 0;
0377     int rightWidthAverage = 0;
0378     foreach (const Private::Row &row, d->m_rows) {
0379         const QWidget *valueWidget = row.value;
0380         const int rightWidth = valueWidget->sizeHint().width();
0381         rightWidthAverage += rightWidth;
0382         if (rightWidth > rightWidthMax) {
0383             rightWidthMax = rightWidth;
0384         }
0385 
0386         const int leftWidth = row.label->sizeHint().width();
0387         if (leftWidth > leftWidthMax) {
0388             leftWidthMax = leftWidth;
0389         }
0390     }
0391 
0392     // Some value widgets might return a very huge width for the size hint.
0393     // Limit the maximum width to the double width of the overall average
0394     // to assure a less messed layout.
0395     if (d->m_rows.count() > 1) {
0396         rightWidthAverage /= d->m_rows.count();
0397         if (rightWidthMax > rightWidthAverage * 2) {
0398             rightWidthMax = rightWidthAverage * 2;
0399         }
0400     }
0401 
0402     // Based on the available width calculate the required height
0403     int height = d->m_gridLayout->margin() * 2 + d->m_gridLayout->spacing() * (d->m_rows.count() - 1);
0404     foreach (const Private::Row &row, d->m_rows) {
0405         const QWidget *valueWidget = row.value;
0406         const int rowHeight = qMax(row.label->heightForWidth(leftWidthMax),
0407                                    valueWidget->heightForWidth(rightWidthMax));
0408         height += rowHeight;
0409     }
0410 
0411     const int width = d->m_gridLayout->margin() * 2 + leftWidthMax +
0412                       d->m_gridLayout->spacing() + rightWidthMax;
0413 
0414     return QSize(width, height);
0415 }
0416 
0417 bool KFileMetaDataWidget::event(QEvent *event)
0418 {
0419     return QWidget::event(event);
0420 }
0421 
0422 #include "moc_kfilemetadatawidget.cpp"