File indexing completed on 2025-01-12 12:26:24
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"